All of lore.kernel.org
 help / color / mirror / Atom feed
* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
@ 2019-03-17  1:21 Christian Stewart
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 2/6] package/runc: upgrade to go modules Christian Stewart
                   ` (6 more replies)
  0 siblings, 7 replies; 20+ messages in thread
From: Christian Stewart @ 2019-03-17  1:21 UTC (permalink / raw)
  To: buildroot

This commit moves from the GOPATH mechanism to the new GO111MODULE approach for
Go based packages. Old Go packages (with the exception of docker-cli) will
compile without changes (for example, mender, flanneld).

Cavets with the current implementation:

 - "make source" may not produce a valid output
 - module downloading occurs ignoring the Buildroot download server
 - module downloading occurs in a post-patch hook

Go code uses a package-based imports system. Imports are
relative to a root directory, previously called GOPATH.

import "github.com/docker/docker/pkg/myutils"

This would resolve to $GOPATH/src/github.com/docker/docker/pkg/myutils.

Buildroot packages previously used an additional feature in the Go tool which
allows packages to avoid using GOPATH by "vendoring" dependencies - copying the
code directly into the Git repository.

vendor/github.com/docker/docker/pkg/myutils

Old packages that used the vendor/ approach remain compatible via inferring the
root import path from the download URL if no go.mod is present.

All current Buildroot Go modules use "vendor" to avoid downloading dependencies.
This requires that the Go projects added to Buildroot include all of their
dependencies in their repositories. It also does not allow us the opportunity to
validate or adjust dependency versions when upgrading packages in Buildroot.

The Go module system aims to completely replace the GOPATH mechanism by
allowing the Go tool to reason about package versioning, code fetching,
proxying, and checksumming.

A project can contain any number of go.mod files. A go.mod file is akin to
Node's package.json file. It specifies all direct and indirect dependencies of
all Go packages in the subtree below the file. The Go tool can manage this file
automatically if desired, and specifies a required format. Go.mod additionally
requires dependency versions to be explicitly specified. There is no semantic
versioning or asterisk-based version specifiers.

module mymodule

require (
github.com/aws/aws-sdk-go v1.17.12 // indirect
github.com/blang/semver v3.5.2-0.20180723201105-3c1074078d32+incompatible
)

The Go tool creates a go.sum file next to the go.mod file. The go.sum
file is akin to Node's package-shrinkwrap.json.

github.com/aws/aws-sdk-go v1.15.31/go.mod
h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.17.12
h1:jMFwRUaM0LcfdenfvbDLePNoWSoCdOHqF4RCvSB4xNQ=
github.com/aws/aws-sdk-go v1.17.12/go.mod
h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/blang/semver
v3.5.2-0.20180723201105-3c1074078d32+incompatible
h1:8fBbhRkI5/0ocLFbrhPgnGUm0ogc+Gko1cRodPWDKX4=
github.com/blang/semver
v3.5.2-0.20180723201105-3c1074078d32+incompatible/go.mod
h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=

The file contains import paths, versions, and file hashes for the download as
well as the contained go.mod file.

With replace directives, it's possible to link together local copies of modules:

replace (
k8s.io/gengo => ./staging/src/k8s.io/gengo
k8s.io/kube-openapi => ./staging/src/k8s.io/kube-openapi
)

When GO111MODULE=on, all of the Go tool commands become "module-aware." GOPATH
is no longer required. Running "go build" for example will fetch dependencies
from the Internet, checksum them, and extract to a staging directory (defaulting
currently to GOPATH/pkg/gomod or so). Imports are automatically resolved to the
appropriately versioned staging directory.

There are various ways to control the download / extract behavior:

  The GOPROXY environment variable allows further control over the
  download source. If GOPROXY is unset, is the empty string, or is the
  string "direct", downloads use the default direct connection to
  version control systems. Setting GOPROXY to "off" disallows
  downloading modules from any source. Otherwise, GOPROXY is expected to
  be the URL of a module proxy, in which case the go command will fetch
  all modules from that proxy. No matter the source of the modules,
  downloaded modules must match existing entries in go.sum...

  Even when downloading directly from version control systems, the go
  command synthesizes explicit info, mod, and zip files and stores them
  in its local cache, $GOPATH/pkg/mod/cache/download, the same as if it
  had downloaded them directly from a proxy. The cache layout is the
  same as the proxy URL space, so serving $GOPATH/pkg/mod/cache/download
  at (or copying it to) https://example.com/proxy would let other users
  access those cached module versions with
  GOPROXY=https://example.com/proxy.

GOPROXY additionally supports file:// URLs.

This commit sets GOPATH to $(DL_DIR)/go-module, as the Go module system will
download and cache code sources in the GOPATH/pkg/mod path.

The go.mod and go.sum files can optionally be placed in $(2)_PKGDIR next to
Config.in and other support files. They are copied in with a post-download hook,
and "go mod download" is executed to pull the dependencies from the internet.

A hook is added to execute "go mod vendor".

Upstream vendor trees are still optionally supported, but can be overridden by
placing go.mod into the Buildroot tree as described above. Package developers
can alternatively specify LIBFOO_GOMOD to indicate the root module path. This
allows the Go module tool to compile code using a legacy vendor/ tree, without a
GOPATH to indicate the import path for the root module.

DOCKER_ENGINE_GOMOD = github.com/docker/docker

A Buildroot user can serve the dl/go-modules directory directly with a HTTP
server and set GOPROXY such that all module downloads come from that server.
This could be configurable eventually via the Buildroot KConfig architecture.

During the build phase, the "-mod=vendor" option is used to indicate that the
extracted vendor/ tree (from the post-extract step) is to be used.

Go modules are never disabled with this approach. They are compatible with the
legacy vendor/ tree approach by the above mechanisms.

Reference: https://github.com/golang/go/wiki/Modules

Signed-off-by: Christian Stewart <christian@paral.in>
---
 package/go/go.mk      |  7 +++++-
 package/pkg-golang.mk | 50 ++++++++++++++++++++++++++++---------------
 2 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/package/go/go.mk b/package/go/go.mk
index 4daa2fe093..be05602f14 100644
--- a/package/go/go.mk
+++ b/package/go/go.mk
@@ -36,6 +36,9 @@ else ifeq ($(BR2_mips64el),y)
 GO_GOARCH = mips64le
 endif
 
+# GOPATH is used only for Go module downloads.
+HOST_GO_GOPATH = $(DL_DIR)/go-module
+
 HOST_GO_DEPENDENCIES = host-go-bootstrap
 HOST_GO_HOST_CACHE = $(HOST_DIR)/usr/share/host-go-cache
 HOST_GO_ROOT = $(HOST_DIR)/lib/go
@@ -44,10 +47,12 @@ HOST_GO_TARGET_CACHE = $(HOST_DIR)/usr/share/go-cache
 # For the convienience of target packages.
 HOST_GO_TOOLDIR = $(HOST_GO_ROOT)/pkg/tool/linux_$(GO_GOARCH)
 HOST_GO_TARGET_ENV = \
-	GO111MODULE=off \
+	GO111MODULE=on \
 	GOARCH=$(GO_GOARCH) \
 	GOCACHE="$(HOST_GO_TARGET_CACHE)" \
+	GOPROXY=off \
 	GOROOT="$(HOST_GO_ROOT)" \
+	GOPATH="$(HOST_GO_GOPATH)" \
 	CC="$(TARGET_CC)" \
 	CXX="$(TARGET_CXX)" \
 	GOTOOLDIR="$(HOST_GO_TOOLDIR)"
diff --git a/package/pkg-golang.mk b/package/pkg-golang.mk
index 4f2c7e77e1..91651c5fca 100644
--- a/package/pkg-golang.mk
+++ b/package/pkg-golang.mk
@@ -57,6 +57,7 @@ endif
 
 $(2)_BUILD_OPTS += \
 	-ldflags "$$($(2)_LDFLAGS)" \
+	-mod=vendor \
 	-tags "$$($(2)_TAGS)" \
 	-p $(PARALLEL_JOBS)
 
@@ -75,38 +76,53 @@ endif
 
 $(2)_INSTALL_BINS ?= $(1)
 
-# Source files in Go should be extracted in a precise folder in the hierarchy
-# of GOPATH. It usually resolves around domain/vendor/software. By default, we
-# derive domain/vendor/software from the upstream URL of the project, but we
-# allow $(2)_SRC_SUBDIR to be overridden if needed.
+# Source files in Go usually use an import path resolved around
+# domain/vendor/software. We infer domain/vendor/software from the upstream URL
+# of the project. $(2)_GOMOD can be overridden.
 $(2)_SRC_DOMAIN = $$(call domain,$$($(2)_SITE))
 $(2)_SRC_VENDOR = $$(word 1,$$(subst /, ,$$(call notdomain,$$($(2)_SITE))))
 $(2)_SRC_SOFTWARE = $$(word 2,$$(subst /, ,$$(call notdomain,$$($(2)_SITE))))
 
-$(2)_SRC_SUBDIR ?= $$($(2)_SRC_DOMAIN)/$$($(2)_SRC_VENDOR)/$$($(2)_SRC_SOFTWARE)
-$(2)_SRC_PATH = $$(@D)/$$($(2)_WORKSPACE)/src/$$($(2)_SRC_SUBDIR)
-
-# Configure step. Only define it if not already defined by the package .mk
-# file.
-ifndef $(2)_CONFIGURE_CMDS
-define $(2)_CONFIGURE_CMDS
-	mkdir -p $$(dir $$($(2)_SRC_PATH))
-	ln -sf $$(@D) $$($(2)_SRC_PATH)
+$(2)_GOMOD ?= $$($(2)_SRC_DOMAIN)/$$($(2)_SRC_VENDOR)/$$($(2)_SRC_SOFTWARE)
+
+# Correctly configure the go.mod and go.sum files for the module system.
+# TODO: GOPROXY: use fallback mechanism and Buildroot proxy
+# TODO: Perform the downloading / vendoring such that "make source" is correct
+define $(2)_APPLY_EXTRACT_GOMOD
+	if [ -f $$($(2)_PKGDIR)/go.mod ]; then \
+		cp $$($(2)_PKGDIR)/go.mod $$(@D)/go.mod; \
+		if [ -f $$(@D)/go.sum ]; then \
+			rm $$(@D)/go.sum; \
+		fi; \
+	fi; \
+	if [ -f $$($(2)_PKGDIR)/go.sum ]; then \
+		cp $$($(2)_PKGDIR)/go.sum $$(@D)/go.sum; \
+	fi
+	if [ ! -f $$(@D)/go.mod ] && [ -n "$$($(2)_GOMOD)" ]; then \
+		printf "module $$($(2)_GOMOD)\n" > $$(@D)/go.mod; \
+	fi
+	if [ ! -d $$(@D)/vendor ]; then \
+		cd $$(@D); \
+		$$(GO_TARGET_ENV) \
+			$$($(2)_GO_ENV) \
+			GOPROXY="direct" \
+			$$(GO_BIN) mod vendor -v; \
+	fi
 endef
-endif
+
+$(2)_POST_EXTRACT_HOOKS += $(2)_APPLY_EXTRACT_GOMOD
 
 # Build step. Only define it if not already defined by the package .mk
 # file.
 ifndef $(2)_BUILD_CMDS
 define $(2)_BUILD_CMDS
 	$$(foreach d,$$($(2)_BUILD_TARGETS),\
-		cd $$($(2)_SRC_PATH); \
+		cd $$(@D); \
 		$$(GO_TARGET_ENV) \
-			GOPATH="$$(@D)/$$($(2)_WORKSPACE)" \
 			$$($(2)_GO_ENV) \
 			$$(GO_BIN) build -v $$($(2)_BUILD_OPTS) \
 			-o $$(@D)/bin/$$(or $$($(2)_BIN_NAME),$$(notdir $$(d))) \
-			./$$(d)
+			$$(d)
 	)
 endef
 endif
-- 
2.19.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 2/6] package/runc: upgrade to go modules
  2019-03-17  1:21 [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Christian Stewart
@ 2019-03-17  1:21 ` Christian Stewart
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 3/6] package/docker-containerd: " Christian Stewart
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 20+ messages in thread
From: Christian Stewart @ 2019-03-17  1:21 UTC (permalink / raw)
  To: buildroot

This commit introduces go.mod and go.sum files derived from the vendor.conf in
the upstream runc repository.

Signed-off-by: Christian Stewart <christian@paral.in>
---
 package/runc/go.mod  | 26 ++++++++++++++++++++++++
 package/runc/go.sum  | 47 ++++++++++++++++++++++++++++++++++++++++++++
 package/runc/runc.mk |  2 --
 3 files changed, 73 insertions(+), 2 deletions(-)
 create mode 100644 package/runc/go.mod
 create mode 100644 package/runc/go.sum

diff --git a/package/runc/go.mod b/package/runc/go.mod
new file mode 100644
index 0000000000..26eed99edb
--- /dev/null
+++ b/package/runc/go.mod
@@ -0,0 +1,26 @@
+module github.com/opencontainers/runc
+
+// Derived from runc @ v1.0.0-rc6.
+require (
+	github.com/checkpoint-restore/go-criu v0.0.0-20181120144056-17b0214f6c48
+	github.com/containerd/console v0.0.0-20180220200639-2748ece16665
+	github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b
+	github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf // indirect
+	github.com/cyphar/filepath-securejoin v0.2.1
+	github.com/docker/go-units v0.2.0
+	github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55
+	github.com/golang/protobuf v0.0.0-20170427213220-18c9bb326172
+	github.com/mrunalp/fileutils v0.0.0-20160610222446-ed869b029674
+	github.com/opencontainers/runtime-spec v0.0.0-20190207185410-29686dbc5559
+	github.com/opencontainers/selinux v1.0.0-rc1
+	github.com/pkg/errors v0.8.0
+	github.com/seccomp/libseccomp-golang v0.0.0-20170424173420-84e90a91acea
+	github.com/sirupsen/logrus v0.0.0-20170713114250-a3f95b5c4235
+	github.com/stretchr/testify v1.3.0 // indirect
+	github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8
+	github.com/urfave/cli v0.0.0-20160917213806-d53eb991652b
+	github.com/vishvananda/netlink v0.0.0-20150820014904-1e2e08e8a2dc
+	github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
+	golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 // indirect
+	golang.org/x/sys v0.0.0-20190204203706-41f3e6584952
+)
diff --git a/package/runc/go.sum b/package/runc/go.sum
new file mode 100644
index 0000000000..b93484b06d
--- /dev/null
+++ b/package/runc/go.sum
@@ -0,0 +1,47 @@
+github.com/checkpoint-restore/go-criu v0.0.0-20181120144056-17b0214f6c48 h1:AQMF0Xixllgf29MKlx/TGEhRk7bEDX5kxz8Ui8lOvEs=
+github.com/checkpoint-restore/go-criu v0.0.0-20181120144056-17b0214f6c48/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho=
+github.com/containerd/console v0.0.0-20180220200639-2748ece16665 h1:8p4sC/YOZbLdxenlXANCdsPv2vt4JiBtYPO9mWql/Wc=
+github.com/containerd/console v0.0.0-20180220200639-2748ece16665/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b h1:+mtZ0WjVZwTX0RVrXMXDwuYVaNeHGvWBW1UwJeMR+2M=
+github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf h1:CAKfRE2YtTUIjjh1bkBtyYFaUT/WmOqsJjgtihT0vMI=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cyphar/filepath-securejoin v0.2.1 h1:5DPkzz/0MwUpvR4fxASKzgApeq2OMFY5FfYtrX28Coo=
+github.com/cyphar/filepath-securejoin v0.2.1/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/docker/go-units v0.2.0 h1:TtZVwKVMsN8COBXUhH/x17NFxEFfIIK2i9DL/nz4zfE=
+github.com/docker/go-units v0.2.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55 h1:oIgNYSrSUbNH5DJh6DMhU1PiOKOYIHNxrV3djLsLpEI=
+github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/golang/protobuf v0.0.0-20170427213220-18c9bb326172 h1:ib1Vbb6/KliPKsRcZdmCUnFGP7/BcCWgW9+gR+sUQk0=
+github.com/golang/protobuf v0.0.0-20170427213220-18c9bb326172/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/mrunalp/fileutils v0.0.0-20160610222446-ed869b029674 h1:6adeeLyL1ODW0umMHTYaU+NkdFgYkHKSYC2cw3MDx2g=
+github.com/mrunalp/fileutils v0.0.0-20160610222446-ed869b029674/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
+github.com/opencontainers/runtime-spec v0.0.0-20190207185410-29686dbc5559 h1:pVIiB5BBYCSqbku9gTus5uZ+dmmZiWtmHAaI8Y1hpb4=
+github.com/opencontainers/runtime-spec v0.0.0-20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/selinux v1.0.0-rc1 h1:Q70KvmpJSrYzryl/d0tC3vWUiTn23cSdStKodlokEPs=
+github.com/opencontainers/selinux v1.0.0-rc1/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
+github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/seccomp/libseccomp-golang v0.0.0-20170424173420-84e90a91acea h1:G1PmHXBlo7d4vTCVEoifjyLZPPe5N7/onNLXgtwsiW0=
+github.com/seccomp/libseccomp-golang v0.0.0-20170424173420-84e90a91acea/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
+github.com/sirupsen/logrus v0.0.0-20170713114250-a3f95b5c4235 h1:a2XWU6egUZQhD52o2GEKr79zE+OuZmwLybyOQpoqhHQ=
+github.com/sirupsen/logrus v0.0.0-20170713114250-a3f95b5c4235/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8 h1:zLV6q4e8Jv9EHjNg/iHfzwDkCve6Ua5jCygptrtXHvI=
+github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/urfave/cli v0.0.0-20160917213806-d53eb991652b h1:NCe8LNa+eGKU8K2/UEbMBM303YWoS0/PrJlbSglCUh0=
+github.com/urfave/cli v0.0.0-20160917213806-d53eb991652b/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/vishvananda/netlink v0.0.0-20150820014904-1e2e08e8a2dc h1:0HAHLwEY4k1VqaO1SzBi4XxT0KA06Cv+QW2LXknBk9g=
+github.com/vishvananda/netlink v0.0.0-20150820014904-1e2e08e8a2dc/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=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190204203706-41f3e6584952 h1:FDfvYgoVsA7TTZSbgiqjAbfPbK47CNHdWl3h/PJtii0=
+golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
diff --git a/package/runc/runc.mk b/package/runc/runc.mk
index 0539661266..4b2675d6e2 100644
--- a/package/runc/runc.mk
+++ b/package/runc/runc.mk
@@ -9,8 +9,6 @@ RUNC_SITE = $(call github,opencontainers,runc,$(RUNC_VERSION))
 RUNC_LICENSE = Apache-2.0
 RUNC_LICENSE_FILES = LICENSE
 
-RUNC_WORKSPACE = Godeps/_workspace
-
 RUNC_LDFLAGS = -X main.gitCommit=$(RUNC_VERSION)
 
 RUNC_TAGS = cgo static_build
-- 
2.19.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 3/6] package/docker-containerd: upgrade to go modules
  2019-03-17  1:21 [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Christian Stewart
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 2/6] package/runc: upgrade to go modules Christian Stewart
@ 2019-03-17  1:21 ` Christian Stewart
  2019-04-05  8:36   ` Arnout Vandecappelle
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 4/6] docker-cli: " Christian Stewart
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Christian Stewart @ 2019-03-17  1:21 UTC (permalink / raw)
  To: buildroot

This commit introduces go.mod and go.sum files derived from the vendor.conf in
the upstream containerd repository.

Signed-off-by: Christian Stewart <christian@paral.in>
---
 .../docker-containerd/docker-containerd.mk    |   9 +-
 package/docker-containerd/go.mod              |  93 ++++++++
 package/docker-containerd/go.sum              | 200 ++++++++++++++++++
 3 files changed, 298 insertions(+), 4 deletions(-)
 create mode 100644 package/docker-containerd/go.mod
 create mode 100644 package/docker-containerd/go.sum

diff --git a/package/docker-containerd/docker-containerd.mk b/package/docker-containerd/docker-containerd.mk
index 9088c17660..18bda643b9 100644
--- a/package/docker-containerd/docker-containerd.mk
+++ b/package/docker-containerd/docker-containerd.mk
@@ -9,12 +9,13 @@ DOCKER_CONTAINERD_SITE = $(call github,containerd,containerd,$(DOCKER_CONTAINERD
 DOCKER_CONTAINERD_LICENSE = Apache-2.0
 DOCKER_CONTAINERD_LICENSE_FILES = LICENSE
 
-DOCKER_CONTAINERD_WORKSPACE = vendor
-
 DOCKER_CONTAINERD_LDFLAGS = \
-	-X github.com/docker/containerd.GitCommit=$(DOCKER_CONTAINERD_VERSION)
+	-X github.com/containerd/containerd.GitCommit=$(DOCKER_CONTAINERD_VERSION)
 
-DOCKER_CONTAINERD_BUILD_TARGETS = cmd/ctr cmd/containerd cmd/containerd-shim
+DOCKER_CONTAINERD_BUILD_TARGETS = \
+	github.com/containerd/containerd/cmd/ctr \
+	github.com/containerd/containerd/cmd/containerd \
+	github.com/containerd/containerd/cmd/containerd-shim
 
 DOCKER_CONTAINERD_INSTALL_BINS = containerd containerd-shim
 
diff --git a/package/docker-containerd/go.mod b/package/docker-containerd/go.mod
new file mode 100644
index 0000000000..03eab35383
--- /dev/null
+++ b/package/docker-containerd/go.mod
@@ -0,0 +1,93 @@
+module github.com/containerd/containerd
+
+// Derived from docker-containerd/vendor.conf @ v1.2.3
+require (
+	github.com/BurntSushi/toml v0.0.0-20170626110600-a368813c5e64
+	github.com/Microsoft/go-winio v0.4.11
+	github.com/Microsoft/hcsshim v0.8.1
+	github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a // indirect
+	github.com/blang/semver v3.1.0+incompatible // indirect
+	github.com/containerd/aufs v0.0.0-20180724161921-ffa39970e26a
+	github.com/containerd/btrfs v0.0.0-20180306195803-2e1aa0ddf94f
+	github.com/containerd/cgroups v0.0.0-20180515175038-5e610833b720
+	github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1
+	github.com/containerd/containerd v1.2.5
+	github.com/containerd/continuity v0.0.0-20181001140422-bd77b46c8352
+	github.com/containerd/cri v0.0.0-20190206004706-c3cf754321fc
+	github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260
+	github.com/containerd/go-cni v0.0.0-20181011142537-40bcf8ec8acd // indirect
+	github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3
+	github.com/containerd/ttrpc v0.0.0-20180920185216-2a805f718635
+	github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd
+	github.com/containerd/zfs v0.0.0-20180329214549-9a0b8b8b5982
+	github.com/containernetworking/cni v0.6.0 // indirect
+	github.com/containernetworking/plugins v0.7.0 // indirect
+	github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b // indirect
+	github.com/docker/distribution v0.0.0-20170213194809-b38e5838b7b2 // indirect
+	github.com/docker/docker v0.0.0-20171019062838-86f080cff091 // indirect
+	github.com/docker/go-events v0.0.0-20170721190031-9461782956ad
+	github.com/docker/go-metrics v0.0.0-20180131145841-4ea375f7759c
+	github.com/docker/go-units v0.3.1
+	github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 // indirect
+	github.com/elazarl/goproxy v0.0.0-20181111060418-2ce16c963a8a // indirect
+	github.com/emicklei/go-restful v2.2.1+incompatible // indirect
+	github.com/ghodss/yaml v1.0.0 // indirect
+	github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55 // indirect
+	github.com/gogo/googleapis v1.0.0
+	github.com/gogo/protobuf v1.0.0
+	github.com/golang/glog v0.0.0-20141105023935-44145f04b68c // indirect
+	github.com/google/go-cmp v0.1.0
+	github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367 // indirect
+	github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20160910222444-6b7015e65d36
+	github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce // indirect
+	github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874 // indirect
+	github.com/json-iterator/go v1.1.5 // indirect
+	github.com/linuxkit/virtsock v0.0.0-20180830132707-8e79449dea07 // indirect
+	github.com/matttproud/golang_protobuf_extensions v1.0.0 // indirect
+	github.com/mistifyio/go-zfs v0.0.0-20171122051224-166add352731 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
+	github.com/onsi/ginkgo v1.8.0 // indirect
+	github.com/onsi/gomega v1.5.0 // indirect
+	github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2
+	github.com/opencontainers/image-spec v1.0.1
+	github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f
+	github.com/opencontainers/runtime-spec v0.0.0-20180909173843-eba862dc2470
+	github.com/opencontainers/runtime-tools v0.6.0 // indirect
+	github.com/opencontainers/selinux v0.0.0-20180628160156-b6fa367ed7f5 // indirect
+	github.com/pborman/uuid v0.0.0-20180122190007-c65b2f87fee3 // indirect
+	github.com/pkg/errors v0.8.0
+	github.com/prometheus/client_golang v0.0.0-20180131142826-f4fb1b73fb09
+	github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5 // indirect
+	github.com/prometheus/common v0.0.0-20180110214958-89604d197083 // indirect
+	github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7 // indirect
+	github.com/satori/go.uuid v1.2.0 // indirect
+	github.com/seccomp/libseccomp-golang v0.0.0-20160531183505-32f571b70023 // indirect
+	github.com/sirupsen/logrus v1.0.0
+	github.com/spf13/pflag v1.0.3 // indirect
+	github.com/stretchr/testify v1.3.0 // indirect
+	github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8
+	github.com/tchap/go-patricia v2.2.6+incompatible // indirect
+	github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5
+	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+	github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f // indirect
+	go.etcd.io/bbolt v1.3.1-etcd.8
+	golang.org/x/crypto v0.0.0-20180222182404-49796115aa4b // indirect
+	golang.org/x/net v0.0.0-20180906233101-161cd47e91fd
+	golang.org/x/oauth2 v0.0.0-20170412232759-a6bd8cefa181 // indirect
+	golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
+	golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e
+	golang.org/x/time v0.0.0-20161028155119-f51c12702a4d // indirect
+	google.golang.org/appengine v1.4.0 // indirect
+	google.golang.org/genproto v0.0.0-20170523043604-d80a6e20e776 // indirect
+	google.golang.org/grpc v1.12.0
+	gopkg.in/inf.v0 v0.9.0 // indirect
+	gotest.tools v2.1.0+incompatible
+	k8s.io/api v0.0.0-20181004124137-fd83cbc87e76 // indirect
+	k8s.io/apimachinery v0.0.0-20180913025736-6dd46049f395 // indirect
+	k8s.io/apiserver v0.0.0-20181004124341-e85ad7b666fe // indirect
+	k8s.io/client-go v9.0.0+incompatible // indirect
+	k8s.io/kubernetes v1.12.0 // indirect
+	k8s.io/utils v0.0.0-20180918230422-cd34563cd63c // indirect
+)
diff --git a/package/docker-containerd/go.sum b/package/docker-containerd/go.sum
new file mode 100644
index 0000000000..41cda7653a
--- /dev/null
+++ b/package/docker-containerd/go.sum
@@ -0,0 +1,200 @@
+github.com/BurntSushi/toml v0.0.0-20170626110600-a368813c5e64 h1:BuYewlQyh/jroxY8qx41SrzD8Go17GkyCyAeVmprvQI=
+github.com/BurntSushi/toml v0.0.0-20170626110600-a368813c5e64/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
+github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/hcsshim v0.8.1 h1:0RKPd1pQB/4YRjdw0jFwq3A5nWFN4n1ojNzcm4B+8ZI=
+github.com/Microsoft/hcsshim v0.8.1/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a h1:BtpsbiV638WQZwhA98cEZw2BsbnQJrbd0BI7tsy0W1c=
+github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/blang/semver v3.1.0+incompatible h1:7hqmJYuaEK3qwVjWubYiht3j93YI0WQBuysxHIfUriU=
+github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/containerd/aufs v0.0.0-20180724161921-ffa39970e26a h1:ixq65h0RN5+oc8h4FrLB6ftleIloflgpphPoqvn3W/4=
+github.com/containerd/aufs v0.0.0-20180724161921-ffa39970e26a/go.mod h1:KWTFEKnST/R6wiSG+n7rid/ZQJxi73594So04IsWRpc=
+github.com/containerd/btrfs v0.0.0-20180306195803-2e1aa0ddf94f h1:ZJ8RlZBVDL4P2MbwK7gizs4a6N06ZsARIjSUaH70Rxk=
+github.com/containerd/btrfs v0.0.0-20180306195803-2e1aa0ddf94f/go.mod h1:obwvRpZTBx8yJQ/6w+6mrVoLAiS0CedRD8CCD+U7vZQ=
+github.com/containerd/cgroups v0.0.0-20180515175038-5e610833b720 h1:BWEq1Q9P+v4Yb97IRvoSMNuHbd2ilBqV9h5Zew3ELco=
+github.com/containerd/cgroups v0.0.0-20180515175038-5e610833b720/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1 h1:uict5mhHFTzKLUCufdSLym7z/J0CbBJT59lYbP9wtbg=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/containerd v1.2.5 h1:D+s0XmoswfcRJXgmMMlI1vAblp+LTCftRnEjKsgbFPU=
+github.com/containerd/containerd v1.2.5/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/continuity v0.0.0-20181001140422-bd77b46c8352 h1:CdBoaTKPl60tksFVWYc5QnLWwXBcU+XcLiXx8+NcZ9o=
+github.com/containerd/continuity v0.0.0-20181001140422-bd77b46c8352/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/cri v0.0.0-20190206004706-c3cf754321fc h1:oaMe0U/5ksRcI1iXLF2Jr1Wy3OukJqUeei2wZl34OPQ=
+github.com/containerd/cri v0.0.0-20190206004706-c3cf754321fc/go.mod h1:DavH5Qa8+6jOmeOMO3dhWoqksucZDe06LfuhBz/xPZs=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 h1:XGyg7oTtD0DoRFhbpV6x1WfV0flKC4UxXU7ab1zC08U=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/go-cni v0.0.0-20181011142537-40bcf8ec8acd h1:joFNf8tTYsguuv6/HMvyPSnMsGloCVSUVDI9TaE4rpM=
+github.com/containerd/go-cni v0.0.0-20181011142537-40bcf8ec8acd/go.mod h1:2wlRxCQdiBY+OcjNg5x8kI+5mEL1fGt25L4IzQHYJsM=
+github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3 h1:esQOJREg8nw8aXj6uCN5dfW5cKUBiEJ/+nni1Q/D/sw=
+github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/ttrpc v0.0.0-20180920185216-2a805f718635 h1:Hh9KYLzbpTyhtCnW4p0Iy+bJNO4fGKFZp1ylELZw6TI=
+github.com/containerd/ttrpc v0.0.0-20180920185216-2a805f718635/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
+github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y=
+github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
+github.com/containerd/zfs v0.0.0-20180329214549-9a0b8b8b5982 h1:+sKKe2jkRCcj+2smvlAh9S5mTv9+ALHPKTGeoEA/e9Q=
+github.com/containerd/zfs v0.0.0-20180329214549-9a0b8b8b5982/go.mod h1:mp4bnlceN4Zxp2fwNMzdKcPMopBajlb+VrHYSpKipcE=
+github.com/containernetworking/cni v0.6.0 h1:FXICGBZNMtdHlW65trpoHviHctQD3seWhRRcqp2hMOU=
+github.com/containernetworking/cni v0.6.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/plugins v0.7.0 h1:CRJEnGMQ20KIsgNU6QZBL1OI9IAGnxyGczHWmDj00Ug=
+github.com/containernetworking/plugins v0.7.0/go.mod h1:dagHaAhNjXjT9QYOklkKJDGaQPTg4pf//FrUcJeb7FU=
+github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b h1:+mtZ0WjVZwTX0RVrXMXDwuYVaNeHGvWBW1UwJeMR+2M=
+github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/docker/distribution v0.0.0-20170213194809-b38e5838b7b2 h1:Ia9z/Prt7e3+tE42GL2CzWkjaJkV2w3TAp/F4rxoAcI=
+github.com/docker/distribution v0.0.0-20170213194809-b38e5838b7b2/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v0.0.0-20171019062838-86f080cff091 h1:QpxpTw4MJeOzbC7X00IFxnZhZx8oDOqXMrMAHiwNn54=
+github.com/docker/docker v0.0.0-20171019062838-86f080cff091/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad h1:VXIse57M5C6ezDuCPyq6QmMvEJ2xclYKZ35SfkXdm3E=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-metrics v0.0.0-20180131145841-4ea375f7759c h1:oKvAcvys1isfMMXIOxB2C4f1VXSFy7PFjsecI//7BMo=
+github.com/docker/go-metrics v0.0.0-20180131145841-4ea375f7759c/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/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
+github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/elazarl/goproxy v0.0.0-20181111060418-2ce16c963a8a h1:A4wNiqeKqU56ZhtnzJCTyPZ1+cyu8jKtIchQ3TtxHgw=
+github.com/elazarl/goproxy v0.0.0-20181111060418-2ce16c963a8a/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/emicklei/go-restful v2.2.1+incompatible h1:yreWt49MQDL5ac0Dau9EKE22or+LrHikXVhAqUAXnfk=
+github.com/emicklei/go-restful v2.2.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55 h1:oIgNYSrSUbNH5DJh6DMhU1PiOKOYIHNxrV3djLsLpEI=
+github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/gogo/googleapis v1.0.0 h1:7wwW6yQ5xmZE42/QWNC87xHgnHxIh7pWvtc1BhI/0DU=
+github.com/gogo/googleapis v1.0.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
+github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM=
+github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20141105023935-44145f04b68c h1:CbdkBQ1/PiAo0FYJhQGwASD8wrgNvTdf01g6+O9tNuA=
+github.com/golang/glog v0.0.0-20141105023935-44145f04b68c/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.1.0 h1:9tmYDKxX2N1am4Ooz6a2HC7DfK0CWNuhT8T/Fi/bvtA=
+github.com/google/go-cmp v0.1.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367 h1:ScAXWS+TR6MZKex+7Z8rneuSJH+FSDqd6ocQyl+ZHo4=
+github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
+github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20160910222444-6b7015e65d36 h1:cwTrrTEhz13khQS3/UZMLFWwiqlcsdp/2sxFmSjAWeQ=
+github.com/grpc-ecosystem/go-grpc-prometheus v0.0.0-20160910222444-6b7015e65d36/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce h1:prjrVgOk2Yg6w+PflHoszQNLTUh4kaByUcEWM/9uin4=
+github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874 h1:cAv7ZbSmyb1wjn6T4TIiyFCkpcfgpbcNNC3bM2srLaI=
+github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
+github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/linuxkit/virtsock v0.0.0-20180830132707-8e79449dea07 h1:Txrn3UR5uEutnv3naHnt9nhDHWDUrK/NjtKs17hMTMw=
+github.com/linuxkit/virtsock v0.0.0-20180830132707-8e79449dea07/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
+github.com/matttproud/golang_protobuf_extensions v1.0.0 h1:YNOwxxSJzSUARoD9KRZLzM9Y858MNGCOACTvCW9TSAc=
+github.com/matttproud/golang_protobuf_extensions v1.0.0/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mistifyio/go-zfs v0.0.0-20171122051224-166add352731 h1:2kI6piV7D1QfQwaJsvDK5+zwDtz5Z5pODaPZbAbIers=
+github.com/mistifyio/go-zfs v0.0.0-20171122051224-166add352731/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
+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 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2 h1:QhPf3A2AZW3tTGvHPg0TA+CR3oHbVLlXUhlghqISp1I=
+github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
+github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f h1:a969LJ4IQFwRHYqonHtUDMSh9i54WcKggeEkQ3fZMl4=
+github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runtime-spec v0.0.0-20180909173843-eba862dc2470 h1:dQgS6CgSB2mBQur4Cz7kaEtXNSw56ZlRb7ZsBT70hTA=
+github.com/opencontainers/runtime-spec v0.0.0-20180909173843-eba862dc2470/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-tools v0.6.0 h1:NYIDT9KoSAIfmXpMrulp/j+64c8OBb2l19u3vmOl4so=
+github.com/opencontainers/runtime-tools v0.6.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+github.com/opencontainers/selinux v0.0.0-20180628160156-b6fa367ed7f5 h1:gB9Q5rgyqOydWjwo1/qd0AEWa0C8MIl+MR8tklNaQ+w=
+github.com/opencontainers/selinux v0.0.0-20180628160156-b6fa367ed7f5/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
+github.com/pborman/uuid v0.0.0-20180122190007-c65b2f87fee3 h1:9J0mOv1rXIBlRjQCiAGyx9C3dZZh5uIa3HU0oTV8v1E=
+github.com/pborman/uuid v0.0.0-20180122190007-c65b2f87fee3/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
+github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.0.0-20180131142826-f4fb1b73fb09 h1:v1e+C2rmzKYWLU13Xu9q2C3IlruVdQGL9hRj9jo97/k=
+github.com/prometheus/client_golang v0.0.0-20180131142826-f4fb1b73fb09/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5 h1:cLL6NowurKLMfCeQy4tIeph12XNQWgANCNvdyrOYKV4=
+github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.0.0-20180110214958-89604d197083 h1:BVsJT8+ZbyuL3hypz/HmEiM8h2P6hBQGig4el9/MdjA=
+github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7 h1:hhvfGDVThBnd4kYisSFmYuHYeUhglxcwag7FhVPH9zM=
+github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/seccomp/libseccomp-golang v0.0.0-20160531183505-32f571b70023 h1:ZtrLs4RfZhyCYqamkjrwNSSAYhnRYqBSi8g17h2kars=
+github.com/seccomp/libseccomp-golang v0.0.0-20160531183505-32f571b70023/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
+github.com/sirupsen/logrus v1.0.0 h1:XM8X4m/9ACaclZMs946FQNEZBZafvToJLTR4007drwo=
+github.com/sirupsen/logrus v1.0.0/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
+github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8 h1:zLV6q4e8Jv9EHjNg/iHfzwDkCve6Ua5jCygptrtXHvI=
+github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck=
+github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
+github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5 h1:MCfT24H3f//U5+UCrZp1/riVO3B50BovxtDiNn0XKkk=
+github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+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=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f h1:mvXjJIHRZyhNuGassLTcXTwjiWq7NmjdavZsUnmFybQ=
+github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
+go.etcd.io/bbolt v1.3.1-etcd.8 h1:6J7QAKqfFBGnU80KRnuQxfjjeE5xAGE/qB810I3FQHQ=
+go.etcd.io/bbolt v1.3.1-etcd.8/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+golang.org/x/crypto v0.0.0-20180222182404-49796115aa4b h1:/GxqO8gbyb+sNnviFY2IIMrtm8vGg6NEJDft68wJY/g=
+golang.org/x/crypto v0.0.0-20180222182404-49796115aa4b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/oauth2 v0.0.0-20170412232759-a6bd8cefa181 h1:/4OaQ4bC66Oq9JDhUnxTjBGt8XBhDuwgMRXHgvfcCUY=
+golang.org/x/oauth2 v0.0.0-20170412232759-a6bd8cefa181/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g=
+golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20170523043604-d80a6e20e776 h1:wVJP1pATLVPNxCz4R2mTO6HUJgfGE0PmIu2E10RuhCw=
+google.golang.org/genproto v0.0.0-20170523043604-d80a6e20e776/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.12.0 h1:Mm8atZtkT+P6R43n/dqNDWkPPu5BwRVu/1rJnJCeZH8=
+google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
+gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
+gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+k8s.io/api v0.0.0-20181004124137-fd83cbc87e76 h1:cGc6jt7tNK7a2WfgNKjxjoU/UXXr9Q7JTqvCupZ+6+Y=
+k8s.io/api v0.0.0-20181004124137-fd83cbc87e76/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
+k8s.io/apimachinery v0.0.0-20180913025736-6dd46049f395 h1:X+c9tYTDc9Pmt+Z1YSMqmUTCYf13VYe1u+ZwzjgpK0M=
+k8s.io/apimachinery v0.0.0-20180913025736-6dd46049f395/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
+k8s.io/apiserver v0.0.0-20181004124341-e85ad7b666fe h1:QNs3vf1GO871H/R8rUYJpiXjp7mplm7d5mI9b30XUGQ=
+k8s.io/apiserver v0.0.0-20181004124341-e85ad7b666fe/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w=
+k8s.io/client-go v9.0.0+incompatible h1:2kqW3X2xQ9SbFvWZjGEHBLlWc1LG9JIJNXWkuqwdZ3A=
+k8s.io/client-go v9.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
+k8s.io/kubernetes v1.12.0 h1:2PjG+1oSb9SSwFkJIH6HZH85CDfJ7hOaMMMQYi1Q+0c=
+k8s.io/kubernetes v1.12.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
+k8s.io/utils v0.0.0-20180918230422-cd34563cd63c h1:1fJozimgUsIqPVvgjlpeh53pMbPFXXFiCoe4CErMFJ0=
+k8s.io/utils v0.0.0-20180918230422-cd34563cd63c/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
-- 
2.19.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 4/6] docker-cli: upgrade to go modules
  2019-03-17  1:21 [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Christian Stewart
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 2/6] package/runc: upgrade to go modules Christian Stewart
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 3/6] package/docker-containerd: " Christian Stewart
@ 2019-03-17  1:21 ` Christian Stewart
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 5/6] docker-proxy: " Christian Stewart
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 20+ messages in thread
From: Christian Stewart @ 2019-03-17  1:21 UTC (permalink / raw)
  To: buildroot

This commit introduces go.mod and go.sum files derived from the vendor.conf in
the upstream Docker cli repository.

Replace directives are added to indicate the correct version of docker/engine to
pull in. Additionally, a patch is added to bring in the upstream modifications
to the vendored spf13/pflag package.

Signed-off-by: Christian Stewart <christian@paral.in>
---
 ...ude-modified-package-under-thirdpart.patch | 5259 +++++++++++++++++
 package/docker-cli/docker-cli.mk              |    3 +-
 package/docker-cli/go.mod                     |  127 +
 package/docker-cli/go.sum                     |  419 ++
 4 files changed, 5806 insertions(+), 2 deletions(-)
 create mode 100644 package/docker-cli/0001-spf13-pflag-include-modified-package-under-thirdpart.patch
 create mode 100644 package/docker-cli/go.mod
 create mode 100644 package/docker-cli/go.sum

diff --git a/package/docker-cli/0001-spf13-pflag-include-modified-package-under-thirdpart.patch b/package/docker-cli/0001-spf13-pflag-include-modified-package-under-thirdpart.patch
new file mode 100644
index 0000000000..22ba7cb7af
--- /dev/null
+++ b/package/docker-cli/0001-spf13-pflag-include-modified-package-under-thirdpart.patch
@@ -0,0 +1,5259 @@
+From c46c6786948f8a183ca3e0717e5a8a258c845712 Mon Sep 17 00:00:00 2001
+From: Christian Stewart <christian@paral.in>
+Date: Sat, 16 Mar 2019 17:19:43 -0700
+Subject: [PATCH] spf13/pflag: include modified package under thirdparty
+
+The Docker upstream codebase has a vendored version of spf13/goflag with in-tree
+modifications. The modifications add a IPNetSliceVar to pflag.FlagSet.
+
+Signed-off-by: Christian Stewart <christian@paral.in>
+---
+ thirdparty/pflag/LICENSE             |   28 +
+ thirdparty/pflag/README.md           |  296 +++++++
+ thirdparty/pflag/bool.go             |   94 ++
+ thirdparty/pflag/bool_slice.go       |  147 ++++
+ thirdparty/pflag/bytes.go            |  209 +++++
+ thirdparty/pflag/count.go            |   96 ++
+ thirdparty/pflag/duration.go         |   86 ++
+ thirdparty/pflag/duration_slice.go   |  128 +++
+ thirdparty/pflag/flag.go             | 1224 ++++++++++++++++++++++++++
+ thirdparty/pflag/float32.go          |   88 ++
+ thirdparty/pflag/float64.go          |   84 ++
+ thirdparty/pflag/go.mod              |    3 +
+ thirdparty/pflag/golangflag.go       |  105 +++
+ thirdparty/pflag/int.go              |   84 ++
+ thirdparty/pflag/int16.go            |   88 ++
+ thirdparty/pflag/int32.go            |   88 ++
+ thirdparty/pflag/int64.go            |   84 ++
+ thirdparty/pflag/int8.go             |   88 ++
+ thirdparty/pflag/int_slice.go        |  128 +++
+ thirdparty/pflag/ip.go               |   94 ++
+ thirdparty/pflag/ip_slice.go         |  148 ++++
+ thirdparty/pflag/ipmask.go           |  122 +++
+ thirdparty/pflag/ipnet.go            |   98 +++
+ thirdparty/pflag/ipnet_slice.go      |  147 ++++
+ thirdparty/pflag/string.go           |   80 ++
+ thirdparty/pflag/string_array.go     |  103 +++
+ thirdparty/pflag/string_slice.go     |  149 ++++
+ thirdparty/pflag/string_to_int.go    |  149 ++++
+ thirdparty/pflag/string_to_string.go |  160 ++++
+ thirdparty/pflag/uint.go             |   88 ++
+ thirdparty/pflag/uint16.go           |   88 ++
+ thirdparty/pflag/uint32.go           |   88 ++
+ thirdparty/pflag/uint64.go           |   88 ++
+ thirdparty/pflag/uint8.go            |   88 ++
+ thirdparty/pflag/uint_slice.go       |  126 +++
+ 35 files changed, 4964 insertions(+)
+ create mode 100644 thirdparty/pflag/LICENSE
+ create mode 100644 thirdparty/pflag/README.md
+ create mode 100644 thirdparty/pflag/bool.go
+ create mode 100644 thirdparty/pflag/bool_slice.go
+ create mode 100644 thirdparty/pflag/bytes.go
+ create mode 100644 thirdparty/pflag/count.go
+ create mode 100644 thirdparty/pflag/duration.go
+ create mode 100644 thirdparty/pflag/duration_slice.go
+ create mode 100644 thirdparty/pflag/flag.go
+ create mode 100644 thirdparty/pflag/float32.go
+ create mode 100644 thirdparty/pflag/float64.go
+ create mode 100644 thirdparty/pflag/go.mod
+ create mode 100644 thirdparty/pflag/golangflag.go
+ create mode 100644 thirdparty/pflag/int.go
+ create mode 100644 thirdparty/pflag/int16.go
+ create mode 100644 thirdparty/pflag/int32.go
+ create mode 100644 thirdparty/pflag/int64.go
+ create mode 100644 thirdparty/pflag/int8.go
+ create mode 100644 thirdparty/pflag/int_slice.go
+ create mode 100644 thirdparty/pflag/ip.go
+ create mode 100644 thirdparty/pflag/ip_slice.go
+ create mode 100644 thirdparty/pflag/ipmask.go
+ create mode 100644 thirdparty/pflag/ipnet.go
+ create mode 100644 thirdparty/pflag/ipnet_slice.go
+ create mode 100644 thirdparty/pflag/string.go
+ create mode 100644 thirdparty/pflag/string_array.go
+ create mode 100644 thirdparty/pflag/string_slice.go
+ create mode 100644 thirdparty/pflag/string_to_int.go
+ create mode 100644 thirdparty/pflag/string_to_string.go
+ create mode 100644 thirdparty/pflag/uint.go
+ create mode 100644 thirdparty/pflag/uint16.go
+ create mode 100644 thirdparty/pflag/uint32.go
+ create mode 100644 thirdparty/pflag/uint64.go
+ create mode 100644 thirdparty/pflag/uint8.go
+ create mode 100644 thirdparty/pflag/uint_slice.go
+
+diff --git a/thirdparty/pflag/LICENSE b/thirdparty/pflag/LICENSE
+new file mode 100644
+index 00000000..63ed1cfe
+--- /dev/null
++++ b/thirdparty/pflag/LICENSE
+@@ -0,0 +1,28 @@
++Copyright (c) 2012 Alex Ogier. All rights reserved.
++Copyright (c) 2012 The Go Authors. All rights reserved.
++
++Redistribution and use in source and binary forms, with or without
++modification, are permitted provided that the following conditions are
++met:
++
++   * Redistributions of source code must retain the above copyright
++notice, this list of conditions and the following disclaimer.
++   * Redistributions in binary form must reproduce the above
++copyright notice, this list of conditions and the following disclaimer
++in the documentation and/or other materials provided with the
++distribution.
++   * Neither the name of Google Inc. nor the names of its
++contributors may be used to endorse or promote products derived from
++this software without specific prior written permission.
++
++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+diff --git a/thirdparty/pflag/README.md b/thirdparty/pflag/README.md
+new file mode 100644
+index 00000000..b052414d
+--- /dev/null
++++ b/thirdparty/pflag/README.md
+@@ -0,0 +1,296 @@
++[![Build Status](https://travis-ci.org/spf13/pflag.svg?branch=master)](https://travis-ci.org/spf13/pflag)
++[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/pflag)](https://goreportcard.com/report/github.com/spf13/pflag)
++[![GoDoc](https://godoc.org/github.com/spf13/pflag?status.svg)](https://godoc.org/github.com/spf13/pflag)
++
++## Description
++
++pflag is a drop-in replacement for Go's flag package, implementing
++POSIX/GNU-style --flags.
++
++pflag is compatible with the [GNU extensions to the POSIX recommendations
++for command-line options][1]. For a more precise description, see the
++"Command-line flag syntax" section below.
++
++[1]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
++
++pflag is available under the same style of BSD license as the Go language,
++which can be found in the LICENSE file.
++
++## Installation
++
++pflag is available using the standard `go get` command.
++
++Install by running:
++
++    go get github.com/spf13/pflag
++
++Run tests by running:
++
++    go test github.com/spf13/pflag
++
++## Usage
++
++pflag is a drop-in replacement of Go's native flag package. If you import
++pflag under the name "flag" then all code should continue to function
++with no changes.
++
++``` go
++import flag "github.com/spf13/pflag"
++```
++
++There is one exception to this: if you directly instantiate the Flag struct
++there is one more field "Shorthand" that you will need to set.
++Most code never instantiates this struct directly, and instead uses
++functions such as String(), BoolVar(), and Var(), and is therefore
++unaffected.
++
++Define flags using flag.String(), Bool(), Int(), etc.
++
++This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
++
++``` go
++var ip *int = flag.Int("flagname", 1234, "help message for flagname")
++```
++
++If you like, you can bind the flag to a variable using the Var() functions.
++
++``` go
++var flagvar int
++func init() {
++    flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
++}
++```
++
++Or you can create custom flags that satisfy the Value interface (with
++pointer receivers) and couple them to flag parsing by
++
++``` go
++flag.Var(&flagVal, "name", "help message for flagname")
++```
++
++For such flags, the default value is just the initial value of the variable.
++
++After all flags are defined, call
++
++``` go
++flag.Parse()
++```
++
++to parse the command line into the defined flags.
++
++Flags may then be used directly. If you're using the flags themselves,
++they are all pointers; if you bind to variables, they're values.
++
++``` go
++fmt.Println("ip has value ", *ip)
++fmt.Println("flagvar has value ", flagvar)
++```
++
++There are helpers function to get values later if you have the FlagSet but
++it was difficult to keep up with all of the flag pointers in your code.
++If you have a pflag.FlagSet with a flag called 'flagname' of type int you
++can use GetInt() to get the int value. But notice that 'flagname' must exist
++and it must be an int. GetString("flagname") will fail.
++
++``` go
++i, err := flagset.GetInt("flagname")
++```
++
++After parsing, the arguments after the flag are available as the
++slice flag.Args() or individually as flag.Arg(i).
++The arguments are indexed from 0 through flag.NArg()-1.
++
++The pflag package also defines some new functions that are not in flag,
++that give one-letter shorthands for flags. You can use these by appending
++'P' to the name of any function that defines a flag.
++
++``` go
++var ip = flag.IntP("flagname", "f", 1234, "help message")
++var flagvar bool
++func init() {
++	flag.BoolVarP(&flagvar, "boolname", "b", true, "help message")
++}
++flag.VarP(&flagVal, "varname", "v", "help message")
++```
++
++Shorthand letters can be used with single dashes on the command line.
++Boolean shorthand flags can be combined with other shorthand flags.
++
++The default set of command-line flags is controlled by
++top-level functions.  The FlagSet type allows one to define
++independent sets of flags, such as to implement subcommands
++in a command-line interface. The methods of FlagSet are
++analogous to the top-level functions for the command-line
++flag set.
++
++## Setting no option default values for flags
++
++After you create a flag it is possible to set the pflag.NoOptDefVal for
++the given flag. Doing this changes the meaning of the flag slightly. If
++a flag has a NoOptDefVal and the flag is set on the command line without
++an option the flag will be set to the NoOptDefVal. For example given:
++
++``` go
++var ip = flag.IntP("flagname", "f", 1234, "help message")
++flag.Lookup("flagname").NoOptDefVal = "4321"
++```
++
++Would result in something like
++
++| Parsed Arguments | Resulting Value |
++| -------------    | -------------   |
++| --flagname=1357  | ip=1357         |
++| --flagname       | ip=4321         |
++| [nothing]        | ip=1234         |
++
++## Command line flag syntax
++
++```
++--flag    // boolean flags, or flags with no option default values
++--flag x  // only on flags without a default value
++--flag=x
++```
++
++Unlike the flag package, a single dash before an option means something
++different than a double dash. Single dashes signify a series of shorthand
++letters for flags. All but the last shorthand letter must be boolean flags
++or a flag with a default value
++
++```
++// boolean or flags where the 'no option default value' is set
++-f
++-f=true
++-abc
++but
++-b true is INVALID
++
++// non-boolean and flags without a 'no option default value'
++-n 1234
++-n=1234
++-n1234
++
++// mixed
++-abcs "hello"
++-absd="hello"
++-abcs1234
++```
++
++Flag parsing stops after the terminator "--". Unlike the flag package,
++flags can be interspersed with arguments anywhere on the command line
++before this terminator.
++
++Integer flags accept 1234, 0664, 0x1234 and may be negative.
++Boolean flags (in their long form) accept 1, 0, t, f, true, false,
++TRUE, FALSE, True, False.
++Duration flags accept any input valid for time.ParseDuration.
++
++## Mutating or "Normalizing" Flag names
++
++It is possible to set a custom flag name 'normalization function.' It allows flag names to be mutated both when created in the code and when used on the command line to some 'normalized' form. The 'normalized' form is used for comparison. Two examples of using the custom normalization func follow.
++
++**Example #1**: You want -, _, and . in flags to compare the same. aka --my-flag == --my_flag == --my.flag
++
++``` go
++func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
++	from := []string{"-", "_"}
++	to := "."
++	for _, sep := range from {
++		name = strings.Replace(name, sep, to, -1)
++	}
++	return pflag.NormalizedName(name)
++}
++
++myFlagSet.SetNormalizeFunc(wordSepNormalizeFunc)
++```
++
++**Example #2**: You want to alias two flags. aka --old-flag-name == --new-flag-name
++
++``` go
++func aliasNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
++	switch name {
++	case "old-flag-name":
++		name = "new-flag-name"
++		break
++	}
++	return pflag.NormalizedName(name)
++}
++
++myFlagSet.SetNormalizeFunc(aliasNormalizeFunc)
++```
++
++## Deprecating a flag or its shorthand
++It is possible to deprecate a flag, or just its shorthand. Deprecating a flag/shorthand hides it from help text and prints a usage message when the deprecated flag/shorthand is used.
++
++**Example #1**: You want to deprecate a flag named "badflag" as well as inform the users what flag they should use instead.
++```go
++// deprecate a flag by specifying its name and a usage message
++flags.MarkDeprecated("badflag", "please use --good-flag instead")
++```
++This hides "badflag" from help text, and prints `Flag --badflag has been deprecated, please use --good-flag instead` when "badflag" is used.
++
++**Example #2**: You want to keep a flag name "noshorthandflag" but deprecate its shortname "n".
++```go
++// deprecate a flag shorthand by specifying its flag name and a usage message
++flags.MarkShorthandDeprecated("noshorthandflag", "please use --noshorthandflag only")
++```
++This hides the shortname "n" from help text, and prints `Flag shorthand -n has been deprecated, please use --noshorthandflag only` when the shorthand "n" is used.
++
++Note that usage message is essential here, and it should not be empty.
++
++## Hidden flags
++It is possible to mark a flag as hidden, meaning it will still function as normal, however will not show up in usage/help text.
++
++**Example**: You have a flag named "secretFlag" that you need for internal use only and don't want it showing up in help text, or for its usage text to be available.
++```go
++// hide a flag by specifying its name
++flags.MarkHidden("secretFlag")
++```
++
++## Disable sorting of flags
++`pflag` allows you to disable sorting of flags for help and usage message.
++
++**Example**:
++```go
++flags.BoolP("verbose", "v", false, "verbose output")
++flags.String("coolflag", "yeaah", "it's really cool flag")
++flags.Int("usefulflag", 777, "sometimes it's very useful")
++flags.SortFlags = false
++flags.PrintDefaults()
++```
++**Output**:
++```
++  -v, --verbose           verbose output
++      --coolflag string   it's really cool flag (default "yeaah")
++      --usefulflag int    sometimes it's very useful (default 777)
++```
++
++
++## Supporting Go flags when using pflag
++In order to support flags defined using Go's `flag` package, they must be added to the `pflag` flagset. This is usually necessary
++to support flags defined by third-party dependencies (e.g. `golang/glog`).
++
++**Example**: You want to add the Go flags to the `CommandLine` flagset
++```go
++import (
++	goflag "flag"
++	flag "github.com/spf13/pflag"
++)
++
++var ip *int = flag.Int("flagname", 1234, "help message for flagname")
++
++func main() {
++	flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
++	flag.Parse()
++}
++```
++
++## More info
++
++You can see the full reference documentation of the pflag package
++[at godoc.org][3], or through go's standard documentation system by
++running `godoc -http=:6060` and browsing to
++[http://localhost:6060/pkg/github.com/spf13/pflag][2] after
++installation.
++
++[2]: http://localhost:6060/pkg/github.com/spf13/pflag
++[3]: http://godoc.org/github.com/spf13/pflag
+diff --git a/thirdparty/pflag/bool.go b/thirdparty/pflag/bool.go
+new file mode 100644
+index 00000000..c4c5c0bf
+--- /dev/null
++++ b/thirdparty/pflag/bool.go
+@@ -0,0 +1,94 @@
++package pflag
++
++import "strconv"
++
++// optional interface to indicate boolean flags that can be
++// supplied without "=value" text
++type boolFlag interface {
++	Value
++	IsBoolFlag() bool
++}
++
++// -- bool Value
++type boolValue bool
++
++func newBoolValue(val bool, p *bool) *boolValue {
++	*p = val
++	return (*boolValue)(p)
++}
++
++func (b *boolValue) Set(s string) error {
++	v, err := strconv.ParseBool(s)
++	*b = boolValue(v)
++	return err
++}
++
++func (b *boolValue) Type() string {
++	return "bool"
++}
++
++func (b *boolValue) String() string { return strconv.FormatBool(bool(*b)) }
++
++func (b *boolValue) IsBoolFlag() bool { return true }
++
++func boolConv(sval string) (interface{}, error) {
++	return strconv.ParseBool(sval)
++}
++
++// GetBool return the bool value of a flag with the given name
++func (f *FlagSet) GetBool(name string) (bool, error) {
++	val, err := f.getFlagType(name, "bool", boolConv)
++	if err != nil {
++		return false, err
++	}
++	return val.(bool), nil
++}
++
++// BoolVar defines a bool flag with specified name, default value, and usage string.
++// The argument p points to a bool variable in which to store the value of the flag.
++func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) {
++	f.BoolVarP(p, name, "", value, usage)
++}
++
++// BoolVarP is like BoolVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) BoolVarP(p *bool, name, shorthand string, value bool, usage string) {
++	flag := f.VarPF(newBoolValue(value, p), name, shorthand, usage)
++	flag.NoOptDefVal = "true"
++}
++
++// BoolVar defines a bool flag with specified name, default value, and usage string.
++// The argument p points to a bool variable in which to store the value of the flag.
++func BoolVar(p *bool, name string, value bool, usage string) {
++	BoolVarP(p, name, "", value, usage)
++}
++
++// BoolVarP is like BoolVar, but accepts a shorthand letter that can be used after a single dash.
++func BoolVarP(p *bool, name, shorthand string, value bool, usage string) {
++	flag := CommandLine.VarPF(newBoolValue(value, p), name, shorthand, usage)
++	flag.NoOptDefVal = "true"
++}
++
++// Bool defines a bool flag with specified name, default value, and usage string.
++// The return value is the address of a bool variable that stores the value of the flag.
++func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
++	return f.BoolP(name, "", value, usage)
++}
++
++// BoolP is like Bool, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) BoolP(name, shorthand string, value bool, usage string) *bool {
++	p := new(bool)
++	f.BoolVarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Bool defines a bool flag with specified name, default value, and usage string.
++// The return value is the address of a bool variable that stores the value of the flag.
++func Bool(name string, value bool, usage string) *bool {
++	return BoolP(name, "", value, usage)
++}
++
++// BoolP is like Bool, but accepts a shorthand letter that can be used after a single dash.
++func BoolP(name, shorthand string, value bool, usage string) *bool {
++	b := CommandLine.BoolP(name, shorthand, value, usage)
++	return b
++}
+diff --git a/thirdparty/pflag/bool_slice.go b/thirdparty/pflag/bool_slice.go
+new file mode 100644
+index 00000000..5af02f1a
+--- /dev/null
++++ b/thirdparty/pflag/bool_slice.go
+@@ -0,0 +1,147 @@
++package pflag
++
++import (
++	"io"
++	"strconv"
++	"strings"
++)
++
++// -- boolSlice Value
++type boolSliceValue struct {
++	value   *[]bool
++	changed bool
++}
++
++func newBoolSliceValue(val []bool, p *[]bool) *boolSliceValue {
++	bsv := new(boolSliceValue)
++	bsv.value = p
++	*bsv.value = val
++	return bsv
++}
++
++// Set converts, and assigns, the comma-separated boolean argument string representation as the []bool value of this flag.
++// If Set is called on a flag that already has a []bool assigned, the newly converted values will be appended.
++func (s *boolSliceValue) Set(val string) error {
++
++	// remove all quote characters
++	rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "")
++
++	// read flag arguments with CSV parser
++	boolStrSlice, err := readAsCSV(rmQuote.Replace(val))
++	if err != nil && err != io.EOF {
++		return err
++	}
++
++	// parse boolean values into slice
++	out := make([]bool, 0, len(boolStrSlice))
++	for _, boolStr := range boolStrSlice {
++		b, err := strconv.ParseBool(strings.TrimSpace(boolStr))
++		if err != nil {
++			return err
++		}
++		out = append(out, b)
++	}
++
++	if !s.changed {
++		*s.value = out
++	} else {
++		*s.value = append(*s.value, out...)
++	}
++
++	s.changed = true
++
++	return nil
++}
++
++// Type returns a string that uniquely represents this flag's type.
++func (s *boolSliceValue) Type() string {
++	return "boolSlice"
++}
++
++// String defines a "native" format for this boolean slice flag value.
++func (s *boolSliceValue) String() string {
++
++	boolStrSlice := make([]string, len(*s.value))
++	for i, b := range *s.value {
++		boolStrSlice[i] = strconv.FormatBool(b)
++	}
++
++	out, _ := writeAsCSV(boolStrSlice)
++
++	return "[" + out + "]"
++}
++
++func boolSliceConv(val string) (interface{}, error) {
++	val = strings.Trim(val, "[]")
++	// Empty string would cause a slice with one (empty) entry
++	if len(val) == 0 {
++		return []bool{}, nil
++	}
++	ss := strings.Split(val, ",")
++	out := make([]bool, len(ss))
++	for i, t := range ss {
++		var err error
++		out[i], err = strconv.ParseBool(t)
++		if err != nil {
++			return nil, err
++		}
++	}
++	return out, nil
++}
++
++// GetBoolSlice returns the []bool value of a flag with the given name.
++func (f *FlagSet) GetBoolSlice(name string) ([]bool, error) {
++	val, err := f.getFlagType(name, "boolSlice", boolSliceConv)
++	if err != nil {
++		return []bool{}, err
++	}
++	return val.([]bool), nil
++}
++
++// BoolSliceVar defines a boolSlice flag with specified name, default value, and usage string.
++// The argument p points to a []bool variable in which to store the value of the flag.
++func (f *FlagSet) BoolSliceVar(p *[]bool, name string, value []bool, usage string) {
++	f.VarP(newBoolSliceValue(value, p), name, "", usage)
++}
++
++// BoolSliceVarP is like BoolSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) BoolSliceVarP(p *[]bool, name, shorthand string, value []bool, usage string) {
++	f.VarP(newBoolSliceValue(value, p), name, shorthand, usage)
++}
++
++// BoolSliceVar defines a []bool flag with specified name, default value, and usage string.
++// The argument p points to a []bool variable in which to store the value of the flag.
++func BoolSliceVar(p *[]bool, name string, value []bool, usage string) {
++	CommandLine.VarP(newBoolSliceValue(value, p), name, "", usage)
++}
++
++// BoolSliceVarP is like BoolSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func BoolSliceVarP(p *[]bool, name, shorthand string, value []bool, usage string) {
++	CommandLine.VarP(newBoolSliceValue(value, p), name, shorthand, usage)
++}
++
++// BoolSlice defines a []bool flag with specified name, default value, and usage string.
++// The return value is the address of a []bool variable that stores the value of the flag.
++func (f *FlagSet) BoolSlice(name string, value []bool, usage string) *[]bool {
++	p := []bool{}
++	f.BoolSliceVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// BoolSliceP is like BoolSlice, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) BoolSliceP(name, shorthand string, value []bool, usage string) *[]bool {
++	p := []bool{}
++	f.BoolSliceVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// BoolSlice defines a []bool flag with specified name, default value, and usage string.
++// The return value is the address of a []bool variable that stores the value of the flag.
++func BoolSlice(name string, value []bool, usage string) *[]bool {
++	return CommandLine.BoolSliceP(name, "", value, usage)
++}
++
++// BoolSliceP is like BoolSlice, but accepts a shorthand letter that can be used after a single dash.
++func BoolSliceP(name, shorthand string, value []bool, usage string) *[]bool {
++	return CommandLine.BoolSliceP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/bytes.go b/thirdparty/pflag/bytes.go
+new file mode 100644
+index 00000000..67d53045
+--- /dev/null
++++ b/thirdparty/pflag/bytes.go
+@@ -0,0 +1,209 @@
++package pflag
++
++import (
++	"encoding/base64"
++	"encoding/hex"
++	"fmt"
++	"strings"
++)
++
++// BytesHex adapts []byte for use as a flag. Value of flag is HEX encoded
++type bytesHexValue []byte
++
++// String implements pflag.Value.String.
++func (bytesHex bytesHexValue) String() string {
++	return fmt.Sprintf("%X", []byte(bytesHex))
++}
++
++// Set implements pflag.Value.Set.
++func (bytesHex *bytesHexValue) Set(value string) error {
++	bin, err := hex.DecodeString(strings.TrimSpace(value))
++
++	if err != nil {
++		return err
++	}
++
++	*bytesHex = bin
++
++	return nil
++}
++
++// Type implements pflag.Value.Type.
++func (*bytesHexValue) Type() string {
++	return "bytesHex"
++}
++
++func newBytesHexValue(val []byte, p *[]byte) *bytesHexValue {
++	*p = val
++	return (*bytesHexValue)(p)
++}
++
++func bytesHexConv(sval string) (interface{}, error) {
++
++	bin, err := hex.DecodeString(sval)
++
++	if err == nil {
++		return bin, nil
++	}
++
++	return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err)
++}
++
++// GetBytesHex return the []byte value of a flag with the given name
++func (f *FlagSet) GetBytesHex(name string) ([]byte, error) {
++	val, err := f.getFlagType(name, "bytesHex", bytesHexConv)
++
++	if err != nil {
++		return []byte{}, err
++	}
++
++	return val.([]byte), nil
++}
++
++// BytesHexVar defines an []byte flag with specified name, default value, and usage string.
++// The argument p points to an []byte variable in which to store the value of the flag.
++func (f *FlagSet) BytesHexVar(p *[]byte, name string, value []byte, usage string) {
++	f.VarP(newBytesHexValue(value, p), name, "", usage)
++}
++
++// BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) {
++	f.VarP(newBytesHexValue(value, p), name, shorthand, usage)
++}
++
++// BytesHexVar defines an []byte flag with specified name, default value, and usage string.
++// The argument p points to an []byte variable in which to store the value of the flag.
++func BytesHexVar(p *[]byte, name string, value []byte, usage string) {
++	CommandLine.VarP(newBytesHexValue(value, p), name, "", usage)
++}
++
++// BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash.
++func BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) {
++	CommandLine.VarP(newBytesHexValue(value, p), name, shorthand, usage)
++}
++
++// BytesHex defines an []byte flag with specified name, default value, and usage string.
++// The return value is the address of an []byte variable that stores the value of the flag.
++func (f *FlagSet) BytesHex(name string, value []byte, usage string) *[]byte {
++	p := new([]byte)
++	f.BytesHexVarP(p, name, "", value, usage)
++	return p
++}
++
++// BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) BytesHexP(name, shorthand string, value []byte, usage string) *[]byte {
++	p := new([]byte)
++	f.BytesHexVarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// BytesHex defines an []byte flag with specified name, default value, and usage string.
++// The return value is the address of an []byte variable that stores the value of the flag.
++func BytesHex(name string, value []byte, usage string) *[]byte {
++	return CommandLine.BytesHexP(name, "", value, usage)
++}
++
++// BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash.
++func BytesHexP(name, shorthand string, value []byte, usage string) *[]byte {
++	return CommandLine.BytesHexP(name, shorthand, value, usage)
++}
++
++// BytesBase64 adapts []byte for use as a flag. Value of flag is Base64 encoded
++type bytesBase64Value []byte
++
++// String implements pflag.Value.String.
++func (bytesBase64 bytesBase64Value) String() string {
++	return base64.StdEncoding.EncodeToString([]byte(bytesBase64))
++}
++
++// Set implements pflag.Value.Set.
++func (bytesBase64 *bytesBase64Value) Set(value string) error {
++	bin, err := base64.StdEncoding.DecodeString(strings.TrimSpace(value))
++
++	if err != nil {
++		return err
++	}
++
++	*bytesBase64 = bin
++
++	return nil
++}
++
++// Type implements pflag.Value.Type.
++func (*bytesBase64Value) Type() string {
++	return "bytesBase64"
++}
++
++func newBytesBase64Value(val []byte, p *[]byte) *bytesBase64Value {
++	*p = val
++	return (*bytesBase64Value)(p)
++}
++
++func bytesBase64ValueConv(sval string) (interface{}, error) {
++
++	bin, err := base64.StdEncoding.DecodeString(sval)
++	if err == nil {
++		return bin, nil
++	}
++
++	return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err)
++}
++
++// GetBytesBase64 return the []byte value of a flag with the given name
++func (f *FlagSet) GetBytesBase64(name string) ([]byte, error) {
++	val, err := f.getFlagType(name, "bytesBase64", bytesBase64ValueConv)
++
++	if err != nil {
++		return []byte{}, err
++	}
++
++	return val.([]byte), nil
++}
++
++// BytesBase64Var defines an []byte flag with specified name, default value, and usage string.
++// The argument p points to an []byte variable in which to store the value of the flag.
++func (f *FlagSet) BytesBase64Var(p *[]byte, name string, value []byte, usage string) {
++	f.VarP(newBytesBase64Value(value, p), name, "", usage)
++}
++
++// BytesBase64VarP is like BytesBase64Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) BytesBase64VarP(p *[]byte, name, shorthand string, value []byte, usage string) {
++	f.VarP(newBytesBase64Value(value, p), name, shorthand, usage)
++}
++
++// BytesBase64Var defines an []byte flag with specified name, default value, and usage string.
++// The argument p points to an []byte variable in which to store the value of the flag.
++func BytesBase64Var(p *[]byte, name string, value []byte, usage string) {
++	CommandLine.VarP(newBytesBase64Value(value, p), name, "", usage)
++}
++
++// BytesBase64VarP is like BytesBase64Var, but accepts a shorthand letter that can be used after a single dash.
++func BytesBase64VarP(p *[]byte, name, shorthand string, value []byte, usage string) {
++	CommandLine.VarP(newBytesBase64Value(value, p), name, shorthand, usage)
++}
++
++// BytesBase64 defines an []byte flag with specified name, default value, and usage string.
++// The return value is the address of an []byte variable that stores the value of the flag.
++func (f *FlagSet) BytesBase64(name string, value []byte, usage string) *[]byte {
++	p := new([]byte)
++	f.BytesBase64VarP(p, name, "", value, usage)
++	return p
++}
++
++// BytesBase64P is like BytesBase64, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) BytesBase64P(name, shorthand string, value []byte, usage string) *[]byte {
++	p := new([]byte)
++	f.BytesBase64VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// BytesBase64 defines an []byte flag with specified name, default value, and usage string.
++// The return value is the address of an []byte variable that stores the value of the flag.
++func BytesBase64(name string, value []byte, usage string) *[]byte {
++	return CommandLine.BytesBase64P(name, "", value, usage)
++}
++
++// BytesBase64P is like BytesBase64, but accepts a shorthand letter that can be used after a single dash.
++func BytesBase64P(name, shorthand string, value []byte, usage string) *[]byte {
++	return CommandLine.BytesBase64P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/count.go b/thirdparty/pflag/count.go
+new file mode 100644
+index 00000000..aa126e44
+--- /dev/null
++++ b/thirdparty/pflag/count.go
+@@ -0,0 +1,96 @@
++package pflag
++
++import "strconv"
++
++// -- count Value
++type countValue int
++
++func newCountValue(val int, p *int) *countValue {
++	*p = val
++	return (*countValue)(p)
++}
++
++func (i *countValue) Set(s string) error {
++	// "+1" means that no specific value was passed, so increment
++	if s == "+1" {
++		*i = countValue(*i + 1)
++		return nil
++	}
++	v, err := strconv.ParseInt(s, 0, 0)
++	*i = countValue(v)
++	return err
++}
++
++func (i *countValue) Type() string {
++	return "count"
++}
++
++func (i *countValue) String() string { return strconv.Itoa(int(*i)) }
++
++func countConv(sval string) (interface{}, error) {
++	i, err := strconv.Atoi(sval)
++	if err != nil {
++		return nil, err
++	}
++	return i, nil
++}
++
++// GetCount return the int value of a flag with the given name
++func (f *FlagSet) GetCount(name string) (int, error) {
++	val, err := f.getFlagType(name, "count", countConv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(int), nil
++}
++
++// CountVar defines a count flag with specified name, default value, and usage string.
++// The argument p points to an int variable in which to store the value of the flag.
++// A count flag will add 1 to its value evey time it is found on the command line
++func (f *FlagSet) CountVar(p *int, name string, usage string) {
++	f.CountVarP(p, name, "", usage)
++}
++
++// CountVarP is like CountVar only take a shorthand for the flag name.
++func (f *FlagSet) CountVarP(p *int, name, shorthand string, usage string) {
++	flag := f.VarPF(newCountValue(0, p), name, shorthand, usage)
++	flag.NoOptDefVal = "+1"
++}
++
++// CountVar like CountVar only the flag is placed on the CommandLine instead of a given flag set
++func CountVar(p *int, name string, usage string) {
++	CommandLine.CountVar(p, name, usage)
++}
++
++// CountVarP is like CountVar only take a shorthand for the flag name.
++func CountVarP(p *int, name, shorthand string, usage string) {
++	CommandLine.CountVarP(p, name, shorthand, usage)
++}
++
++// Count defines a count flag with specified name, default value, and usage string.
++// The return value is the address of an int variable that stores the value of the flag.
++// A count flag will add 1 to its value evey time it is found on the command line
++func (f *FlagSet) Count(name string, usage string) *int {
++	p := new(int)
++	f.CountVarP(p, name, "", usage)
++	return p
++}
++
++// CountP is like Count only takes a shorthand for the flag name.
++func (f *FlagSet) CountP(name, shorthand string, usage string) *int {
++	p := new(int)
++	f.CountVarP(p, name, shorthand, usage)
++	return p
++}
++
++// Count defines a count flag with specified name, default value, and usage string.
++// The return value is the address of an int variable that stores the value of the flag.
++// A count flag will add 1 to its value evey time it is found on the command line
++func Count(name string, usage string) *int {
++	return CommandLine.CountP(name, "", usage)
++}
++
++// CountP is like Count only takes a shorthand for the flag name.
++func CountP(name, shorthand string, usage string) *int {
++	return CommandLine.CountP(name, shorthand, usage)
++}
+diff --git a/thirdparty/pflag/duration.go b/thirdparty/pflag/duration.go
+new file mode 100644
+index 00000000..e9debef8
+--- /dev/null
++++ b/thirdparty/pflag/duration.go
+@@ -0,0 +1,86 @@
++package pflag
++
++import (
++	"time"
++)
++
++// -- time.Duration Value
++type durationValue time.Duration
++
++func newDurationValue(val time.Duration, p *time.Duration) *durationValue {
++	*p = val
++	return (*durationValue)(p)
++}
++
++func (d *durationValue) Set(s string) error {
++	v, err := time.ParseDuration(s)
++	*d = durationValue(v)
++	return err
++}
++
++func (d *durationValue) Type() string {
++	return "duration"
++}
++
++func (d *durationValue) String() string { return (*time.Duration)(d).String() }
++
++func durationConv(sval string) (interface{}, error) {
++	return time.ParseDuration(sval)
++}
++
++// GetDuration return the duration value of a flag with the given name
++func (f *FlagSet) GetDuration(name string) (time.Duration, error) {
++	val, err := f.getFlagType(name, "duration", durationConv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(time.Duration), nil
++}
++
++// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
++// The argument p points to a time.Duration variable in which to store the value of the flag.
++func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
++	f.VarP(newDurationValue(value, p), name, "", usage)
++}
++
++// DurationVarP is like DurationVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string) {
++	f.VarP(newDurationValue(value, p), name, shorthand, usage)
++}
++
++// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
++// The argument p points to a time.Duration variable in which to store the value of the flag.
++func DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
++	CommandLine.VarP(newDurationValue(value, p), name, "", usage)
++}
++
++// DurationVarP is like DurationVar, but accepts a shorthand letter that can be used after a single dash.
++func DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string) {
++	CommandLine.VarP(newDurationValue(value, p), name, shorthand, usage)
++}
++
++// Duration defines a time.Duration flag with specified name, default value, and usage string.
++// The return value is the address of a time.Duration variable that stores the value of the flag.
++func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration {
++	p := new(time.Duration)
++	f.DurationVarP(p, name, "", value, usage)
++	return p
++}
++
++// DurationP is like Duration, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) DurationP(name, shorthand string, value time.Duration, usage string) *time.Duration {
++	p := new(time.Duration)
++	f.DurationVarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Duration defines a time.Duration flag with specified name, default value, and usage string.
++// The return value is the address of a time.Duration variable that stores the value of the flag.
++func Duration(name string, value time.Duration, usage string) *time.Duration {
++	return CommandLine.DurationP(name, "", value, usage)
++}
++
++// DurationP is like Duration, but accepts a shorthand letter that can be used after a single dash.
++func DurationP(name, shorthand string, value time.Duration, usage string) *time.Duration {
++	return CommandLine.DurationP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/duration_slice.go b/thirdparty/pflag/duration_slice.go
+new file mode 100644
+index 00000000..52c6b6dc
+--- /dev/null
++++ b/thirdparty/pflag/duration_slice.go
+@@ -0,0 +1,128 @@
++package pflag
++
++import (
++	"fmt"
++	"strings"
++	"time"
++)
++
++// -- durationSlice Value
++type durationSliceValue struct {
++	value   *[]time.Duration
++	changed bool
++}
++
++func newDurationSliceValue(val []time.Duration, p *[]time.Duration) *durationSliceValue {
++	dsv := new(durationSliceValue)
++	dsv.value = p
++	*dsv.value = val
++	return dsv
++}
++
++func (s *durationSliceValue) Set(val string) error {
++	ss := strings.Split(val, ",")
++	out := make([]time.Duration, len(ss))
++	for i, d := range ss {
++		var err error
++		out[i], err = time.ParseDuration(d)
++		if err != nil {
++			return err
++		}
++
++	}
++	if !s.changed {
++		*s.value = out
++	} else {
++		*s.value = append(*s.value, out...)
++	}
++	s.changed = true
++	return nil
++}
++
++func (s *durationSliceValue) Type() string {
++	return "durationSlice"
++}
++
++func (s *durationSliceValue) String() string {
++	out := make([]string, len(*s.value))
++	for i, d := range *s.value {
++		out[i] = fmt.Sprintf("%s", d)
++	}
++	return "[" + strings.Join(out, ",") + "]"
++}
++
++func durationSliceConv(val string) (interface{}, error) {
++	val = strings.Trim(val, "[]")
++	// Empty string would cause a slice with one (empty) entry
++	if len(val) == 0 {
++		return []time.Duration{}, nil
++	}
++	ss := strings.Split(val, ",")
++	out := make([]time.Duration, len(ss))
++	for i, d := range ss {
++		var err error
++		out[i], err = time.ParseDuration(d)
++		if err != nil {
++			return nil, err
++		}
++
++	}
++	return out, nil
++}
++
++// GetDurationSlice returns the []time.Duration value of a flag with the given name
++func (f *FlagSet) GetDurationSlice(name string) ([]time.Duration, error) {
++	val, err := f.getFlagType(name, "durationSlice", durationSliceConv)
++	if err != nil {
++		return []time.Duration{}, err
++	}
++	return val.([]time.Duration), nil
++}
++
++// DurationSliceVar defines a durationSlice flag with specified name, default value, and usage string.
++// The argument p points to a []time.Duration variable in which to store the value of the flag.
++func (f *FlagSet) DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) {
++	f.VarP(newDurationSliceValue(value, p), name, "", usage)
++}
++
++// DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) {
++	f.VarP(newDurationSliceValue(value, p), name, shorthand, usage)
++}
++
++// DurationSliceVar defines a duration[] flag with specified name, default value, and usage string.
++// The argument p points to a duration[] variable in which to store the value of the flag.
++func DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) {
++	CommandLine.VarP(newDurationSliceValue(value, p), name, "", usage)
++}
++
++// DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) {
++	CommandLine.VarP(newDurationSliceValue(value, p), name, shorthand, usage)
++}
++
++// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string.
++// The return value is the address of a []time.Duration variable that stores the value of the flag.
++func (f *FlagSet) DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration {
++	p := []time.Duration{}
++	f.DurationSliceVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration {
++	p := []time.Duration{}
++	f.DurationSliceVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// DurationSlice defines a []time.Duration flag with specified name, default value, and usage string.
++// The return value is the address of a []time.Duration variable that stores the value of the flag.
++func DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration {
++	return CommandLine.DurationSliceP(name, "", value, usage)
++}
++
++// DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash.
++func DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration {
++	return CommandLine.DurationSliceP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/flag.go b/thirdparty/pflag/flag.go
+new file mode 100644
+index 00000000..5cc710cc
+--- /dev/null
++++ b/thirdparty/pflag/flag.go
+@@ -0,0 +1,1224 @@
++// Copyright 2009 The Go Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style
++// license that can be found in the LICENSE file.
++
++/*
++Package pflag is a drop-in replacement for Go's flag package, implementing
++POSIX/GNU-style --flags.
++
++pflag is compatible with the GNU extensions to the POSIX recommendations
++for command-line options. See
++http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
++
++Usage:
++
++pflag is a drop-in replacement of Go's native flag package. If you import
++pflag under the name "flag" then all code should continue to function
++with no changes.
++
++	import flag "github.com/spf13/pflag"
++
++There is one exception to this: if you directly instantiate the Flag struct
++there is one more field "Shorthand" that you will need to set.
++Most code never instantiates this struct directly, and instead uses
++functions such as String(), BoolVar(), and Var(), and is therefore
++unaffected.
++
++Define flags using flag.String(), Bool(), Int(), etc.
++
++This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
++	var ip = flag.Int("flagname", 1234, "help message for flagname")
++If you like, you can bind the flag to a variable using the Var() functions.
++	var flagvar int
++	func init() {
++		flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
++	}
++Or you can create custom flags that satisfy the Value interface (with
++pointer receivers) and couple them to flag parsing by
++	flag.Var(&flagVal, "name", "help message for flagname")
++For such flags, the default value is just the initial value of the variable.
++
++After all flags are defined, call
++	flag.Parse()
++to parse the command line into the defined flags.
++
++Flags may then be used directly. If you're using the flags themselves,
++they are all pointers; if you bind to variables, they're values.
++	fmt.Println("ip has value ", *ip)
++	fmt.Println("flagvar has value ", flagvar)
++
++After parsing, the arguments after the flag are available as the
++slice flag.Args() or individually as flag.Arg(i).
++The arguments are indexed from 0 through flag.NArg()-1.
++
++The pflag package also defines some new functions that are not in flag,
++that give one-letter shorthands for flags. You can use these by appending
++'P' to the name of any function that defines a flag.
++	var ip = flag.IntP("flagname", "f", 1234, "help message")
++	var flagvar bool
++	func init() {
++		flag.BoolVarP("boolname", "b", true, "help message")
++	}
++	flag.VarP(&flagVar, "varname", "v", 1234, "help message")
++Shorthand letters can be used with single dashes on the command line.
++Boolean shorthand flags can be combined with other shorthand flags.
++
++Command line flag syntax:
++	--flag    // boolean flags only
++	--flag=x
++
++Unlike the flag package, a single dash before an option means something
++different than a double dash. Single dashes signify a series of shorthand
++letters for flags. All but the last shorthand letter must be boolean flags.
++	// boolean flags
++	-f
++	-abc
++	// non-boolean flags
++	-n 1234
++	-Ifile
++	// mixed
++	-abcs "hello"
++	-abcn1234
++
++Flag parsing stops after the terminator "--". Unlike the flag package,
++flags can be interspersed with arguments anywhere on the command line
++before this terminator.
++
++Integer flags accept 1234, 0664, 0x1234 and may be negative.
++Boolean flags (in their long form) accept 1, 0, t, f, true, false,
++TRUE, FALSE, True, False.
++Duration flags accept any input valid for time.ParseDuration.
++
++The default set of command-line flags is controlled by
++top-level functions.  The FlagSet type allows one to define
++independent sets of flags, such as to implement subcommands
++in a command-line interface. The methods of FlagSet are
++analogous to the top-level functions for the command-line
++flag set.
++*/
++package pflag
++
++import (
++	"bytes"
++	"errors"
++	goflag "flag"
++	"fmt"
++	"io"
++	"os"
++	"sort"
++	"strings"
++)
++
++// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined.
++var ErrHelp = errors.New("pflag: help requested")
++
++// ErrorHandling defines how to handle flag parsing errors.
++type ErrorHandling int
++
++const (
++	// ContinueOnError will return an err from Parse() if an error is found
++	ContinueOnError ErrorHandling = iota
++	// ExitOnError will call os.Exit(2) if an error is found when parsing
++	ExitOnError
++	// PanicOnError will panic() if an error is found when parsing flags
++	PanicOnError
++)
++
++// ParseErrorsWhitelist defines the parsing errors that can be ignored
++type ParseErrorsWhitelist struct {
++	// UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags
++	UnknownFlags bool
++}
++
++// NormalizedName is a flag name that has been normalized according to rules
++// for the FlagSet (e.g. making '-' and '_' equivalent).
++type NormalizedName string
++
++// A FlagSet represents a set of defined flags.
++type FlagSet struct {
++	// Usage is the function called when an error occurs while parsing flags.
++	// The field is a function (not a method) that may be changed to point to
++	// a custom error handler.
++	Usage func()
++
++	// SortFlags is used to indicate, if user wants to have sorted flags in
++	// help/usage messages.
++	SortFlags bool
++
++	// ParseErrorsWhitelist is used to configure a whitelist of errors
++	ParseErrorsWhitelist ParseErrorsWhitelist
++
++	name              string
++	parsed            bool
++	actual            map[NormalizedName]*Flag
++	orderedActual     []*Flag
++	sortedActual      []*Flag
++	formal            map[NormalizedName]*Flag
++	orderedFormal     []*Flag
++	sortedFormal      []*Flag
++	shorthands        map[byte]*Flag
++	args              []string // arguments after flags
++	argsLenAtDash     int      // len(args) when a '--' was located when parsing, or -1 if no --
++	errorHandling     ErrorHandling
++	output            io.Writer // nil means stderr; use out() accessor
++	interspersed      bool      // allow interspersed option/non-option args
++	normalizeNameFunc func(f *FlagSet, name string) NormalizedName
++
++	addedGoFlagSets []*goflag.FlagSet
++}
++
++// A Flag represents the state of a flag.
++type Flag struct {
++	Name                string              // name as it appears on command line
++	Shorthand           string              // one-letter abbreviated flag
++	Usage               string              // help message
++	Value               Value               // value as set
++	DefValue            string              // default value (as text); for usage message
++	Changed             bool                // If the user set the value (or if left to default)
++	NoOptDefVal         string              // default value (as text); if the flag is on the command line without any options
++	Deprecated          string              // If this flag is deprecated, this string is the new or now thing to use
++	Hidden              bool                // used by cobra.Command to allow flags to be hidden from help/usage text
++	ShorthandDeprecated string              // If the shorthand of this flag is deprecated, this string is the new or now thing to use
++	Annotations         map[string][]string // used by cobra.Command bash autocomple code
++}
++
++// Value is the interface to the dynamic value stored in a flag.
++// (The default value is represented as a string.)
++type Value interface {
++	String() string
++	Set(string) error
++	Type() string
++}
++
++// sortFlags returns the flags as a slice in lexicographical sorted order.
++func sortFlags(flags map[NormalizedName]*Flag) []*Flag {
++	list := make(sort.StringSlice, len(flags))
++	i := 0
++	for k := range flags {
++		list[i] = string(k)
++		i++
++	}
++	list.Sort()
++	result := make([]*Flag, len(list))
++	for i, name := range list {
++		result[i] = flags[NormalizedName(name)]
++	}
++	return result
++}
++
++// SetNormalizeFunc allows you to add a function which can translate flag names.
++// Flags added to the FlagSet will be translated and then when anything tries to
++// look up the flag that will also be translated. So it would be possible to create
++// a flag named "getURL" and have it translated to "geturl".  A user could then pass
++// "--getUrl" which may also be translated to "geturl" and everything will work.
++func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) {
++	f.normalizeNameFunc = n
++	f.sortedFormal = f.sortedFormal[:0]
++	for fname, flag := range f.formal {
++		nname := f.normalizeFlagName(flag.Name)
++		if fname == nname {
++			continue
++		}
++		flag.Name = string(nname)
++		delete(f.formal, fname)
++		f.formal[nname] = flag
++		if _, set := f.actual[fname]; set {
++			delete(f.actual, fname)
++			f.actual[nname] = flag
++		}
++	}
++}
++
++// GetNormalizeFunc returns the previously set NormalizeFunc of a function which
++// does no translation, if not set previously.
++func (f *FlagSet) GetNormalizeFunc() func(f *FlagSet, name string) NormalizedName {
++	if f.normalizeNameFunc != nil {
++		return f.normalizeNameFunc
++	}
++	return func(f *FlagSet, name string) NormalizedName { return NormalizedName(name) }
++}
++
++func (f *FlagSet) normalizeFlagName(name string) NormalizedName {
++	n := f.GetNormalizeFunc()
++	return n(f, name)
++}
++
++func (f *FlagSet) out() io.Writer {
++	if f.output == nil {
++		return os.Stderr
++	}
++	return f.output
++}
++
++// SetOutput sets the destination for usage and error messages.
++// If output is nil, os.Stderr is used.
++func (f *FlagSet) SetOutput(output io.Writer) {
++	f.output = output
++}
++
++// VisitAll visits the flags in lexicographical order or
++// in primordial order if f.SortFlags is false, calling fn for each.
++// It visits all flags, even those not set.
++func (f *FlagSet) VisitAll(fn func(*Flag)) {
++	if len(f.formal) == 0 {
++		return
++	}
++
++	var flags []*Flag
++	if f.SortFlags {
++		if len(f.formal) != len(f.sortedFormal) {
++			f.sortedFormal = sortFlags(f.formal)
++		}
++		flags = f.sortedFormal
++	} else {
++		flags = f.orderedFormal
++	}
++
++	for _, flag := range flags {
++		fn(flag)
++	}
++}
++
++// HasFlags returns a bool to indicate if the FlagSet has any flags defined.
++func (f *FlagSet) HasFlags() bool {
++	return len(f.formal) > 0
++}
++
++// HasAvailableFlags returns a bool to indicate if the FlagSet has any flags
++// that are not hidden.
++func (f *FlagSet) HasAvailableFlags() bool {
++	for _, flag := range f.formal {
++		if !flag.Hidden {
++			return true
++		}
++	}
++	return false
++}
++
++// VisitAll visits the command-line flags in lexicographical order or
++// in primordial order if f.SortFlags is false, calling fn for each.
++// It visits all flags, even those not set.
++func VisitAll(fn func(*Flag)) {
++	CommandLine.VisitAll(fn)
++}
++
++// Visit visits the flags in lexicographical order or
++// in primordial order if f.SortFlags is false, calling fn for each.
++// It visits only those flags that have been set.
++func (f *FlagSet) Visit(fn func(*Flag)) {
++	if len(f.actual) == 0 {
++		return
++	}
++
++	var flags []*Flag
++	if f.SortFlags {
++		if len(f.actual) != len(f.sortedActual) {
++			f.sortedActual = sortFlags(f.actual)
++		}
++		flags = f.sortedActual
++	} else {
++		flags = f.orderedActual
++	}
++
++	for _, flag := range flags {
++		fn(flag)
++	}
++}
++
++// Visit visits the command-line flags in lexicographical order or
++// in primordial order if f.SortFlags is false, calling fn for each.
++// It visits only those flags that have been set.
++func Visit(fn func(*Flag)) {
++	CommandLine.Visit(fn)
++}
++
++// Lookup returns the Flag structure of the named flag, returning nil if none exists.
++func (f *FlagSet) Lookup(name string) *Flag {
++	return f.lookup(f.normalizeFlagName(name))
++}
++
++// ShorthandLookup returns the Flag structure of the short handed flag,
++// returning nil if none exists.
++// It panics, if len(name) > 1.
++func (f *FlagSet) ShorthandLookup(name string) *Flag {
++	if name == "" {
++		return nil
++	}
++	if len(name) > 1 {
++		msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name)
++		fmt.Fprintf(f.out(), msg)
++		panic(msg)
++	}
++	c := name[0]
++	return f.shorthands[c]
++}
++
++// lookup returns the Flag structure of the named flag, returning nil if none exists.
++func (f *FlagSet) lookup(name NormalizedName) *Flag {
++	return f.formal[name]
++}
++
++// func to return a given type for a given flag name
++func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) {
++	flag := f.Lookup(name)
++	if flag == nil {
++		err := fmt.Errorf("flag accessed but not defined: %s", name)
++		return nil, err
++	}
++
++	if flag.Value.Type() != ftype {
++		err := fmt.Errorf("trying to get %s value of flag of type %s", ftype, flag.Value.Type())
++		return nil, err
++	}
++
++	sval := flag.Value.String()
++	result, err := convFunc(sval)
++	if err != nil {
++		return nil, err
++	}
++	return result, nil
++}
++
++// ArgsLenAtDash will return the length of f.Args at the moment when a -- was
++// found during arg parsing. This allows your program to know which args were
++// before the -- and which came after.
++func (f *FlagSet) ArgsLenAtDash() int {
++	return f.argsLenAtDash
++}
++
++// MarkDeprecated indicated that a flag is deprecated in your program. It will
++// continue to function but will not show up in help or usage messages. Using
++// this flag will also print the given usageMessage.
++func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
++	flag := f.Lookup(name)
++	if flag == nil {
++		return fmt.Errorf("flag %q does not exist", name)
++	}
++	if usageMessage == "" {
++		return fmt.Errorf("deprecated message for flag %q must be set", name)
++	}
++	flag.Deprecated = usageMessage
++	flag.Hidden = true
++	return nil
++}
++
++// MarkShorthandDeprecated will mark the shorthand of a flag deprecated in your
++// program. It will continue to function but will not show up in help or usage
++// messages. Using this flag will also print the given usageMessage.
++func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error {
++	flag := f.Lookup(name)
++	if flag == nil {
++		return fmt.Errorf("flag %q does not exist", name)
++	}
++	if usageMessage == "" {
++		return fmt.Errorf("deprecated message for flag %q must be set", name)
++	}
++	flag.ShorthandDeprecated = usageMessage
++	return nil
++}
++
++// MarkHidden sets a flag to 'hidden' in your program. It will continue to
++// function but will not show up in help or usage messages.
++func (f *FlagSet) MarkHidden(name string) error {
++	flag := f.Lookup(name)
++	if flag == nil {
++		return fmt.Errorf("flag %q does not exist", name)
++	}
++	flag.Hidden = true
++	return nil
++}
++
++// Lookup returns the Flag structure of the named command-line flag,
++// returning nil if none exists.
++func Lookup(name string) *Flag {
++	return CommandLine.Lookup(name)
++}
++
++// ShorthandLookup returns the Flag structure of the short handed flag,
++// returning nil if none exists.
++func ShorthandLookup(name string) *Flag {
++	return CommandLine.ShorthandLookup(name)
++}
++
++// Set sets the value of the named flag.
++func (f *FlagSet) Set(name, value string) error {
++	normalName := f.normalizeFlagName(name)
++	flag, ok := f.formal[normalName]
++	if !ok {
++		return fmt.Errorf("no such flag -%v", name)
++	}
++
++	err := flag.Value.Set(value)
++	if err != nil {
++		var flagName string
++		if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
++			flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name)
++		} else {
++			flagName = fmt.Sprintf("--%s", flag.Name)
++		}
++		return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err)
++	}
++
++	if !flag.Changed {
++		if f.actual == nil {
++			f.actual = make(map[NormalizedName]*Flag)
++		}
++		f.actual[normalName] = flag
++		f.orderedActual = append(f.orderedActual, flag)
++
++		flag.Changed = true
++	}
++
++	if flag.Deprecated != "" {
++		fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
++	}
++	return nil
++}
++
++// SetAnnotation allows one to set arbitrary annotations on a flag in the FlagSet.
++// This is sometimes used by spf13/cobra programs which want to generate additional
++// bash completion information.
++func (f *FlagSet) SetAnnotation(name, key string, values []string) error {
++	normalName := f.normalizeFlagName(name)
++	flag, ok := f.formal[normalName]
++	if !ok {
++		return fmt.Errorf("no such flag -%v", name)
++	}
++	if flag.Annotations == nil {
++		flag.Annotations = map[string][]string{}
++	}
++	flag.Annotations[key] = values
++	return nil
++}
++
++// Changed returns true if the flag was explicitly set during Parse() and false
++// otherwise
++func (f *FlagSet) Changed(name string) bool {
++	flag := f.Lookup(name)
++	// If a flag doesn't exist, it wasn't changed....
++	if flag == nil {
++		return false
++	}
++	return flag.Changed
++}
++
++// Set sets the value of the named command-line flag.
++func Set(name, value string) error {
++	return CommandLine.Set(name, value)
++}
++
++// PrintDefaults prints, to standard error unless configured
++// otherwise, the default values of all defined flags in the set.
++func (f *FlagSet) PrintDefaults() {
++	usages := f.FlagUsages()
++	fmt.Fprint(f.out(), usages)
++}
++
++// defaultIsZeroValue returns true if the default value for this flag represents
++// a zero value.
++func (f *Flag) defaultIsZeroValue() bool {
++	switch f.Value.(type) {
++	case boolFlag:
++		return f.DefValue == "false"
++	case *durationValue:
++		// Beginning in Go 1.7, duration zero values are "0s"
++		return f.DefValue == "0" || f.DefValue == "0s"
++	case *intValue, *int8Value, *int32Value, *int64Value, *uintValue, *uint8Value, *uint16Value, *uint32Value, *uint64Value, *countValue, *float32Value, *float64Value:
++		return f.DefValue == "0"
++	case *stringValue:
++		return f.DefValue == ""
++	case *ipValue, *ipMaskValue, *ipNetValue:
++		return f.DefValue == "<nil>"
++	case *intSliceValue, *stringSliceValue, *stringArrayValue:
++		return f.DefValue == "[]"
++	default:
++		switch f.Value.String() {
++		case "false":
++			return true
++		case "<nil>":
++			return true
++		case "":
++			return true
++		case "0":
++			return true
++		}
++		return false
++	}
++}
++
++// UnquoteUsage extracts a back-quoted name from the usage
++// string for a flag and returns it and the un-quoted usage.
++// Given "a `name` to show" it returns ("name", "a name to show").
++// If there are no back quotes, the name is an educated guess of the
++// type of the flag's value, or the empty string if the flag is boolean.
++func UnquoteUsage(flag *Flag) (name string, usage string) {
++	// Look for a back-quoted name, but avoid the strings package.
++	usage = flag.Usage
++	for i := 0; i < len(usage); i++ {
++		if usage[i] == '`' {
++			for j := i + 1; j < len(usage); j++ {
++				if usage[j] == '`' {
++					name = usage[i+1 : j]
++					usage = usage[:i] + name + usage[j+1:]
++					return name, usage
++				}
++			}
++			break // Only one back quote; use type name.
++		}
++	}
++
++	name = flag.Value.Type()
++	switch name {
++	case "bool":
++		name = ""
++	case "float64":
++		name = "float"
++	case "int64":
++		name = "int"
++	case "uint64":
++		name = "uint"
++	case "stringSlice":
++		name = "strings"
++	case "intSlice":
++		name = "ints"
++	case "uintSlice":
++		name = "uints"
++	case "boolSlice":
++		name = "bools"
++	}
++
++	return
++}
++
++// Splits the string `s` on whitespace into an initial substring up to
++// `i` runes in length and the remainder. Will go `slop` over `i` if
++// that encompasses the entire string (which allows the caller to
++// avoid short orphan words on the final line).
++func wrapN(i, slop int, s string) (string, string) {
++	if i+slop > len(s) {
++		return s, ""
++	}
++
++	w := strings.LastIndexAny(s[:i], " \t\n")
++	if w <= 0 {
++		return s, ""
++	}
++	nlPos := strings.LastIndex(s[:i], "\n")
++	if nlPos > 0 && nlPos < w {
++		return s[:nlPos], s[nlPos+1:]
++	}
++	return s[:w], s[w+1:]
++}
++
++// Wraps the string `s` to a maximum width `w` with leading indent
++// `i`. The first line is not indented (this is assumed to be done by
++// caller). Pass `w` == 0 to do no wrapping
++func wrap(i, w int, s string) string {
++	if w == 0 {
++		return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1)
++	}
++
++	// space between indent i and end of line width w into which
++	// we should wrap the text.
++	wrap := w - i
++
++	var r, l string
++
++	// Not enough space for sensible wrapping. Wrap as a block on
++	// the next line instead.
++	if wrap < 24 {
++		i = 16
++		wrap = w - i
++		r += "\n" + strings.Repeat(" ", i)
++	}
++	// If still not enough space then don't even try to wrap.
++	if wrap < 24 {
++		return strings.Replace(s, "\n", r, -1)
++	}
++
++	// Try to avoid short orphan words on the final line, by
++	// allowing wrapN to go a bit over if that would fit in the
++	// remainder of the line.
++	slop := 5
++	wrap = wrap - slop
++
++	// Handle first line, which is indented by the caller (or the
++	// special case above)
++	l, s = wrapN(wrap, slop, s)
++	r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1)
++
++	// Now wrap the rest
++	for s != "" {
++		var t string
++
++		t, s = wrapN(wrap, slop, s)
++		r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1)
++	}
++
++	return r
++
++}
++
++// FlagUsagesWrapped returns a string containing the usage information
++// for all flags in the FlagSet. Wrapped to `cols` columns (0 for no
++// wrapping)
++func (f *FlagSet) FlagUsagesWrapped(cols int) string {
++	buf := new(bytes.Buffer)
++
++	lines := make([]string, 0, len(f.formal))
++
++	maxlen := 0
++	f.VisitAll(func(flag *Flag) {
++		if flag.Hidden {
++			return
++		}
++
++		line := ""
++		if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
++			line = fmt.Sprintf("  -%s, --%s", flag.Shorthand, flag.Name)
++		} else {
++			line = fmt.Sprintf("      --%s", flag.Name)
++		}
++
++		varname, usage := UnquoteUsage(flag)
++		if varname != "" {
++			line += " " + varname
++		}
++		if flag.NoOptDefVal != "" {
++			switch flag.Value.Type() {
++			case "string":
++				line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)
++			case "bool":
++				if flag.NoOptDefVal != "true" {
++					line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
++				}
++			case "count":
++				if flag.NoOptDefVal != "+1" {
++					line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
++				}
++			default:
++				line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
++			}
++		}
++
++		// This special character will be replaced with spacing once the
++		// correct alignment is calculated
++		line += "\x00"
++		if len(line) > maxlen {
++			maxlen = len(line)
++		}
++
++		line += usage
++		if !flag.defaultIsZeroValue() {
++			if flag.Value.Type() == "string" {
++				line += fmt.Sprintf(" (default %q)", flag.DefValue)
++			} else {
++				line += fmt.Sprintf(" (default %s)", flag.DefValue)
++			}
++		}
++		if len(flag.Deprecated) != 0 {
++			line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated)
++		}
++
++		lines = append(lines, line)
++	})
++
++	for _, line := range lines {
++		sidx := strings.Index(line, "\x00")
++		spacing := strings.Repeat(" ", maxlen-sidx)
++		// maxlen + 2 comes from + 1 for the \x00 and + 1 for the (deliberate) off-by-one in maxlen-sidx
++		fmt.Fprintln(buf, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:]))
++	}
++
++	return buf.String()
++}
++
++// FlagUsages returns a string containing the usage information for all flags in
++// the FlagSet
++func (f *FlagSet) FlagUsages() string {
++	return f.FlagUsagesWrapped(0)
++}
++
++// PrintDefaults prints to standard error the default values of all defined command-line flags.
++func PrintDefaults() {
++	CommandLine.PrintDefaults()
++}
++
++// defaultUsage is the default function to print a usage message.
++func defaultUsage(f *FlagSet) {
++	fmt.Fprintf(f.out(), "Usage of %s:\n", f.name)
++	f.PrintDefaults()
++}
++
++// NOTE: Usage is not just defaultUsage(CommandLine)
++// because it serves (via godoc flag Usage) as the example
++// for how to write your own usage function.
++
++// Usage prints to standard error a usage message documenting all defined command-line flags.
++// The function is a variable that may be changed to point to a custom function.
++// By default it prints a simple header and calls PrintDefaults; for details about the
++// format of the output and how to control it, see the documentation for PrintDefaults.
++var Usage = func() {
++	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
++	PrintDefaults()
++}
++
++// NFlag returns the number of flags that have been set.
++func (f *FlagSet) NFlag() int { return len(f.actual) }
++
++// NFlag returns the number of command-line flags that have been set.
++func NFlag() int { return len(CommandLine.actual) }
++
++// Arg returns the i'th argument.  Arg(0) is the first remaining argument
++// after flags have been processed.
++func (f *FlagSet) Arg(i int) string {
++	if i < 0 || i >= len(f.args) {
++		return ""
++	}
++	return f.args[i]
++}
++
++// Arg returns the i'th command-line argument.  Arg(0) is the first remaining argument
++// after flags have been processed.
++func Arg(i int) string {
++	return CommandLine.Arg(i)
++}
++
++// NArg is the number of arguments remaining after flags have been processed.
++func (f *FlagSet) NArg() int { return len(f.args) }
++
++// NArg is the number of arguments remaining after flags have been processed.
++func NArg() int { return len(CommandLine.args) }
++
++// Args returns the non-flag arguments.
++func (f *FlagSet) Args() []string { return f.args }
++
++// Args returns the non-flag command-line arguments.
++func Args() []string { return CommandLine.args }
++
++// Var defines a flag with the specified name and usage string. The type and
++// value of the flag are represented by the first argument, of type Value, which
++// typically holds a user-defined implementation of Value. For instance, the
++// caller could create a flag that turns a comma-separated string into a slice
++// of strings by giving the slice the methods of Value; in particular, Set would
++// decompose the comma-separated string into the slice.
++func (f *FlagSet) Var(value Value, name string, usage string) {
++	f.VarP(value, name, "", usage)
++}
++
++// VarPF is like VarP, but returns the flag created
++func (f *FlagSet) VarPF(value Value, name, shorthand, usage string) *Flag {
++	// Remember the default value as a string; it won't change.
++	flag := &Flag{
++		Name:      name,
++		Shorthand: shorthand,
++		Usage:     usage,
++		Value:     value,
++		DefValue:  value.String(),
++	}
++	f.AddFlag(flag)
++	return flag
++}
++
++// VarP is like Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) VarP(value Value, name, shorthand, usage string) {
++	f.VarPF(value, name, shorthand, usage)
++}
++
++// AddFlag will add the flag to the FlagSet
++func (f *FlagSet) AddFlag(flag *Flag) {
++	normalizedFlagName := f.normalizeFlagName(flag.Name)
++
++	_, alreadyThere := f.formal[normalizedFlagName]
++	if alreadyThere {
++		msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name)
++		fmt.Fprintln(f.out(), msg)
++		panic(msg) // Happens only if flags are declared with identical names
++	}
++	if f.formal == nil {
++		f.formal = make(map[NormalizedName]*Flag)
++	}
++
++	flag.Name = string(normalizedFlagName)
++	f.formal[normalizedFlagName] = flag
++	f.orderedFormal = append(f.orderedFormal, flag)
++
++	if flag.Shorthand == "" {
++		return
++	}
++	if len(flag.Shorthand) > 1 {
++		msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand)
++		fmt.Fprintf(f.out(), msg)
++		panic(msg)
++	}
++	if f.shorthands == nil {
++		f.shorthands = make(map[byte]*Flag)
++	}
++	c := flag.Shorthand[0]
++	used, alreadyThere := f.shorthands[c]
++	if alreadyThere {
++		msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name)
++		fmt.Fprintf(f.out(), msg)
++		panic(msg)
++	}
++	f.shorthands[c] = flag
++}
++
++// AddFlagSet adds one FlagSet to another. If a flag is already present in f
++// the flag from newSet will be ignored.
++func (f *FlagSet) AddFlagSet(newSet *FlagSet) {
++	if newSet == nil {
++		return
++	}
++	newSet.VisitAll(func(flag *Flag) {
++		if f.Lookup(flag.Name) == nil {
++			f.AddFlag(flag)
++		}
++	})
++}
++
++// Var defines a flag with the specified name and usage string. The type and
++// value of the flag are represented by the first argument, of type Value, which
++// typically holds a user-defined implementation of Value. For instance, the
++// caller could create a flag that turns a comma-separated string into a slice
++// of strings by giving the slice the methods of Value; in particular, Set would
++// decompose the comma-separated string into the slice.
++func Var(value Value, name string, usage string) {
++	CommandLine.VarP(value, name, "", usage)
++}
++
++// VarP is like Var, but accepts a shorthand letter that can be used after a single dash.
++func VarP(value Value, name, shorthand, usage string) {
++	CommandLine.VarP(value, name, shorthand, usage)
++}
++
++// failf prints to standard error a formatted error and usage message and
++// returns the error.
++func (f *FlagSet) failf(format string, a ...interface{}) error {
++	err := fmt.Errorf(format, a...)
++	if f.errorHandling != ContinueOnError {
++		fmt.Fprintln(f.out(), err)
++		f.usage()
++	}
++	return err
++}
++
++// usage calls the Usage method for the flag set, or the usage function if
++// the flag set is CommandLine.
++func (f *FlagSet) usage() {
++	if f == CommandLine {
++		Usage()
++	} else if f.Usage == nil {
++		defaultUsage(f)
++	} else {
++		f.Usage()
++	}
++}
++
++//--unknown (args will be empty)
++//--unknown --next-flag ... (args will be --next-flag ...)
++//--unknown arg ... (args will be arg ...)
++func stripUnknownFlagValue(args []string) []string {
++	if len(args) == 0 {
++		//--unknown
++		return args
++	}
++
++	first := args[0]
++	if first[0] == '-' {
++		//--unknown --next-flag ...
++		return args
++	}
++
++	//--unknown arg ... (args will be arg ...)
++	return args[1:]
++}
++
++func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) {
++	a = args
++	name := s[2:]
++	if len(name) == 0 || name[0] == '-' || name[0] == '=' {
++		err = f.failf("bad flag syntax: %s", s)
++		return
++	}
++
++	split := strings.SplitN(name, "=", 2)
++	name = split[0]
++	flag, exists := f.formal[f.normalizeFlagName(name)]
++
++	if !exists {
++		switch {
++		case name == "help":
++			f.usage()
++			return a, ErrHelp
++		case f.ParseErrorsWhitelist.UnknownFlags:
++			// --unknown=unknownval arg ...
++			// we do not want to lose arg in this case
++			if len(split) >= 2 {
++				return a, nil
++			}
++
++			return stripUnknownFlagValue(a), nil
++		default:
++			err = f.failf("unknown flag: --%s", name)
++			return
++		}
++	}
++
++	var value string
++	if len(split) == 2 {
++		// '--flag=arg'
++		value = split[1]
++	} else if flag.NoOptDefVal != "" {
++		// '--flag' (arg was optional)
++		value = flag.NoOptDefVal
++	} else if len(a) > 0 {
++		// '--flag arg'
++		value = a[0]
++		a = a[1:]
++	} else {
++		// '--flag' (arg was required)
++		err = f.failf("flag needs an argument: %s", s)
++		return
++	}
++
++	err = fn(flag, value)
++	if err != nil {
++		f.failf(err.Error())
++	}
++	return
++}
++
++func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) {
++	outArgs = args
++
++	if strings.HasPrefix(shorthands, "test.") {
++		return
++	}
++
++	outShorts = shorthands[1:]
++	c := shorthands[0]
++
++	flag, exists := f.shorthands[c]
++	if !exists {
++		switch {
++		case c == 'h':
++			f.usage()
++			err = ErrHelp
++			return
++		case f.ParseErrorsWhitelist.UnknownFlags:
++			// '-f=arg arg ...'
++			// we do not want to lose arg in this case
++			if len(shorthands) > 2 && shorthands[1] == '=' {
++				outShorts = ""
++				return
++			}
++
++			outArgs = stripUnknownFlagValue(outArgs)
++			return
++		default:
++			err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands)
++			return
++		}
++	}
++
++	var value string
++	if len(shorthands) > 2 && shorthands[1] == '=' {
++		// '-f=arg'
++		value = shorthands[2:]
++		outShorts = ""
++	} else if flag.NoOptDefVal != "" {
++		// '-f' (arg was optional)
++		value = flag.NoOptDefVal
++	} else if len(shorthands) > 1 {
++		// '-farg'
++		value = shorthands[1:]
++		outShorts = ""
++	} else if len(args) > 0 {
++		// '-f arg'
++		value = args[0]
++		outArgs = args[1:]
++	} else {
++		// '-f' (arg was required)
++		err = f.failf("flag needs an argument: %q in -%s", c, shorthands)
++		return
++	}
++
++	if flag.ShorthandDeprecated != "" {
++		fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated)
++	}
++
++	err = fn(flag, value)
++	if err != nil {
++		f.failf(err.Error())
++	}
++	return
++}
++
++func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []string, err error) {
++	a = args
++	shorthands := s[1:]
++
++	// "shorthands" can be a series of shorthand letters of flags (e.g. "-vvv").
++	for len(shorthands) > 0 {
++		shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn)
++		if err != nil {
++			return
++		}
++	}
++
++	return
++}
++
++func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) {
++	for len(args) > 0 {
++		s := args[0]
++		args = args[1:]
++		if len(s) == 0 || s[0] != '-' || len(s) == 1 {
++			if !f.interspersed {
++				f.args = append(f.args, s)
++				f.args = append(f.args, args...)
++				return nil
++			}
++			f.args = append(f.args, s)
++			continue
++		}
++
++		if s[1] == '-' {
++			if len(s) == 2 { // "--" terminates the flags
++				f.argsLenAtDash = len(f.args)
++				f.args = append(f.args, args...)
++				break
++			}
++			args, err = f.parseLongArg(s, args, fn)
++		} else {
++			args, err = f.parseShortArg(s, args, fn)
++		}
++		if err != nil {
++			return
++		}
++	}
++	return
++}
++
++// Parse parses flag definitions from the argument list, which should not
++// include the command name.  Must be called after all flags in the FlagSet
++// are defined and before flags are accessed by the program.
++// The return value will be ErrHelp if -help was set but not defined.
++func (f *FlagSet) Parse(arguments []string) error {
++	if f.addedGoFlagSets != nil {
++		for _, goFlagSet := range f.addedGoFlagSets {
++			goFlagSet.Parse(nil)
++		}
++	}
++	f.parsed = true
++
++	if len(arguments) < 0 {
++		return nil
++	}
++
++	f.args = make([]string, 0, len(arguments))
++
++	set := func(flag *Flag, value string) error {
++		return f.Set(flag.Name, value)
++	}
++
++	err := f.parseArgs(arguments, set)
++	if err != nil {
++		switch f.errorHandling {
++		case ContinueOnError:
++			return err
++		case ExitOnError:
++			fmt.Println(err)
++			os.Exit(2)
++		case PanicOnError:
++			panic(err)
++		}
++	}
++	return nil
++}
++
++type parseFunc func(flag *Flag, value string) error
++
++// ParseAll parses flag definitions from the argument list, which should not
++// include the command name. The arguments for fn are flag and value. Must be
++// called after all flags in the FlagSet are defined and before flags are
++// accessed by the program. The return value will be ErrHelp if -help was set
++// but not defined.
++func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string) error) error {
++	f.parsed = true
++	f.args = make([]string, 0, len(arguments))
++
++	err := f.parseArgs(arguments, fn)
++	if err != nil {
++		switch f.errorHandling {
++		case ContinueOnError:
++			return err
++		case ExitOnError:
++			os.Exit(2)
++		case PanicOnError:
++			panic(err)
++		}
++	}
++	return nil
++}
++
++// Parsed reports whether f.Parse has been called.
++func (f *FlagSet) Parsed() bool {
++	return f.parsed
++}
++
++// Parse parses the command-line flags from os.Args[1:].  Must be called
++// after all flags are defined and before flags are accessed by the program.
++func Parse() {
++	// Ignore errors; CommandLine is set for ExitOnError.
++	CommandLine.Parse(os.Args[1:])
++}
++
++// ParseAll parses the command-line flags from os.Args[1:] and called fn for each.
++// The arguments for fn are flag and value. Must be called after all flags are
++// defined and before flags are accessed by the program.
++func ParseAll(fn func(flag *Flag, value string) error) {
++	// Ignore errors; CommandLine is set for ExitOnError.
++	CommandLine.ParseAll(os.Args[1:], fn)
++}
++
++// SetInterspersed sets whether to support interspersed option/non-option arguments.
++func SetInterspersed(interspersed bool) {
++	CommandLine.SetInterspersed(interspersed)
++}
++
++// Parsed returns true if the command-line flags have been parsed.
++func Parsed() bool {
++	return CommandLine.Parsed()
++}
++
++// CommandLine is the default set of command-line flags, parsed from os.Args.
++var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
++
++// NewFlagSet returns a new, empty flag set with the specified name,
++// error handling property and SortFlags set to true.
++func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
++	f := &FlagSet{
++		name:          name,
++		errorHandling: errorHandling,
++		argsLenAtDash: -1,
++		interspersed:  true,
++		SortFlags:     true,
++	}
++	return f
++}
++
++// SetInterspersed sets whether to support interspersed option/non-option arguments.
++func (f *FlagSet) SetInterspersed(interspersed bool) {
++	f.interspersed = interspersed
++}
++
++// Init sets the name and error handling property for a flag set.
++// By default, the zero FlagSet uses an empty name and the
++// ContinueOnError error handling policy.
++func (f *FlagSet) Init(name string, errorHandling ErrorHandling) {
++	f.name = name
++	f.errorHandling = errorHandling
++	f.argsLenAtDash = -1
++}
+diff --git a/thirdparty/pflag/float32.go b/thirdparty/pflag/float32.go
+new file mode 100644
+index 00000000..a243f81f
+--- /dev/null
++++ b/thirdparty/pflag/float32.go
+@@ -0,0 +1,88 @@
++package pflag
++
++import "strconv"
++
++// -- float32 Value
++type float32Value float32
++
++func newFloat32Value(val float32, p *float32) *float32Value {
++	*p = val
++	return (*float32Value)(p)
++}
++
++func (f *float32Value) Set(s string) error {
++	v, err := strconv.ParseFloat(s, 32)
++	*f = float32Value(v)
++	return err
++}
++
++func (f *float32Value) Type() string {
++	return "float32"
++}
++
++func (f *float32Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 32) }
++
++func float32Conv(sval string) (interface{}, error) {
++	v, err := strconv.ParseFloat(sval, 32)
++	if err != nil {
++		return 0, err
++	}
++	return float32(v), nil
++}
++
++// GetFloat32 return the float32 value of a flag with the given name
++func (f *FlagSet) GetFloat32(name string) (float32, error) {
++	val, err := f.getFlagType(name, "float32", float32Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(float32), nil
++}
++
++// Float32Var defines a float32 flag with specified name, default value, and usage string.
++// The argument p points to a float32 variable in which to store the value of the flag.
++func (f *FlagSet) Float32Var(p *float32, name string, value float32, usage string) {
++	f.VarP(newFloat32Value(value, p), name, "", usage)
++}
++
++// Float32VarP is like Float32Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Float32VarP(p *float32, name, shorthand string, value float32, usage string) {
++	f.VarP(newFloat32Value(value, p), name, shorthand, usage)
++}
++
++// Float32Var defines a float32 flag with specified name, default value, and usage string.
++// The argument p points to a float32 variable in which to store the value of the flag.
++func Float32Var(p *float32, name string, value float32, usage string) {
++	CommandLine.VarP(newFloat32Value(value, p), name, "", usage)
++}
++
++// Float32VarP is like Float32Var, but accepts a shorthand letter that can be used after a single dash.
++func Float32VarP(p *float32, name, shorthand string, value float32, usage string) {
++	CommandLine.VarP(newFloat32Value(value, p), name, shorthand, usage)
++}
++
++// Float32 defines a float32 flag with specified name, default value, and usage string.
++// The return value is the address of a float32 variable that stores the value of the flag.
++func (f *FlagSet) Float32(name string, value float32, usage string) *float32 {
++	p := new(float32)
++	f.Float32VarP(p, name, "", value, usage)
++	return p
++}
++
++// Float32P is like Float32, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Float32P(name, shorthand string, value float32, usage string) *float32 {
++	p := new(float32)
++	f.Float32VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Float32 defines a float32 flag with specified name, default value, and usage string.
++// The return value is the address of a float32 variable that stores the value of the flag.
++func Float32(name string, value float32, usage string) *float32 {
++	return CommandLine.Float32P(name, "", value, usage)
++}
++
++// Float32P is like Float32, but accepts a shorthand letter that can be used after a single dash.
++func Float32P(name, shorthand string, value float32, usage string) *float32 {
++	return CommandLine.Float32P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/float64.go b/thirdparty/pflag/float64.go
+new file mode 100644
+index 00000000..04b5492a
+--- /dev/null
++++ b/thirdparty/pflag/float64.go
+@@ -0,0 +1,84 @@
++package pflag
++
++import "strconv"
++
++// -- float64 Value
++type float64Value float64
++
++func newFloat64Value(val float64, p *float64) *float64Value {
++	*p = val
++	return (*float64Value)(p)
++}
++
++func (f *float64Value) Set(s string) error {
++	v, err := strconv.ParseFloat(s, 64)
++	*f = float64Value(v)
++	return err
++}
++
++func (f *float64Value) Type() string {
++	return "float64"
++}
++
++func (f *float64Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) }
++
++func float64Conv(sval string) (interface{}, error) {
++	return strconv.ParseFloat(sval, 64)
++}
++
++// GetFloat64 return the float64 value of a flag with the given name
++func (f *FlagSet) GetFloat64(name string) (float64, error) {
++	val, err := f.getFlagType(name, "float64", float64Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(float64), nil
++}
++
++// Float64Var defines a float64 flag with specified name, default value, and usage string.
++// The argument p points to a float64 variable in which to store the value of the flag.
++func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) {
++	f.VarP(newFloat64Value(value, p), name, "", usage)
++}
++
++// Float64VarP is like Float64Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Float64VarP(p *float64, name, shorthand string, value float64, usage string) {
++	f.VarP(newFloat64Value(value, p), name, shorthand, usage)
++}
++
++// Float64Var defines a float64 flag with specified name, default value, and usage string.
++// The argument p points to a float64 variable in which to store the value of the flag.
++func Float64Var(p *float64, name string, value float64, usage string) {
++	CommandLine.VarP(newFloat64Value(value, p), name, "", usage)
++}
++
++// Float64VarP is like Float64Var, but accepts a shorthand letter that can be used after a single dash.
++func Float64VarP(p *float64, name, shorthand string, value float64, usage string) {
++	CommandLine.VarP(newFloat64Value(value, p), name, shorthand, usage)
++}
++
++// Float64 defines a float64 flag with specified name, default value, and usage string.
++// The return value is the address of a float64 variable that stores the value of the flag.
++func (f *FlagSet) Float64(name string, value float64, usage string) *float64 {
++	p := new(float64)
++	f.Float64VarP(p, name, "", value, usage)
++	return p
++}
++
++// Float64P is like Float64, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Float64P(name, shorthand string, value float64, usage string) *float64 {
++	p := new(float64)
++	f.Float64VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Float64 defines a float64 flag with specified name, default value, and usage string.
++// The return value is the address of a float64 variable that stores the value of the flag.
++func Float64(name string, value float64, usage string) *float64 {
++	return CommandLine.Float64P(name, "", value, usage)
++}
++
++// Float64P is like Float64, but accepts a shorthand letter that can be used after a single dash.
++func Float64P(name, shorthand string, value float64, usage string) *float64 {
++	return CommandLine.Float64P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/go.mod b/thirdparty/pflag/go.mod
+new file mode 100644
+index 00000000..551ee17d
+--- /dev/null
++++ b/thirdparty/pflag/go.mod
+@@ -0,0 +1,3 @@
++// Go upstream includes a modified pflag in vendor
++// Added: IPNetSliceVar to pflag.FlagSet.
++module github.com/spf13/pflag
+diff --git a/thirdparty/pflag/golangflag.go b/thirdparty/pflag/golangflag.go
+new file mode 100644
+index 00000000..d3dd72b7
+--- /dev/null
++++ b/thirdparty/pflag/golangflag.go
+@@ -0,0 +1,105 @@
++// Copyright 2009 The Go Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style
++// license that can be found in the LICENSE file.
++
++package pflag
++
++import (
++	goflag "flag"
++	"reflect"
++	"strings"
++)
++
++// flagValueWrapper implements pflag.Value around a flag.Value.  The main
++// difference here is the addition of the Type method that returns a string
++// name of the type.  As this is generally unknown, we approximate that with
++// reflection.
++type flagValueWrapper struct {
++	inner    goflag.Value
++	flagType string
++}
++
++// We are just copying the boolFlag interface out of goflag as that is what
++// they use to decide if a flag should get "true" when no arg is given.
++type goBoolFlag interface {
++	goflag.Value
++	IsBoolFlag() bool
++}
++
++func wrapFlagValue(v goflag.Value) Value {
++	// If the flag.Value happens to also be a pflag.Value, just use it directly.
++	if pv, ok := v.(Value); ok {
++		return pv
++	}
++
++	pv := &flagValueWrapper{
++		inner: v,
++	}
++
++	t := reflect.TypeOf(v)
++	if t.Kind() == reflect.Interface || t.Kind() == reflect.Ptr {
++		t = t.Elem()
++	}
++
++	pv.flagType = strings.TrimSuffix(t.Name(), "Value")
++	return pv
++}
++
++func (v *flagValueWrapper) String() string {
++	return v.inner.String()
++}
++
++func (v *flagValueWrapper) Set(s string) error {
++	return v.inner.Set(s)
++}
++
++func (v *flagValueWrapper) Type() string {
++	return v.flagType
++}
++
++// PFlagFromGoFlag will return a *pflag.Flag given a *flag.Flag
++// If the *flag.Flag.Name was a single character (ex: `v`) it will be accessiblei
++// with both `-v` and `--v` in flags. If the golang flag was more than a single
++// character (ex: `verbose`) it will only be accessible via `--verbose`
++func PFlagFromGoFlag(goflag *goflag.Flag) *Flag {
++	// Remember the default value as a string; it won't change.
++	flag := &Flag{
++		Name:  goflag.Name,
++		Usage: goflag.Usage,
++		Value: wrapFlagValue(goflag.Value),
++		// Looks like golang flags don't set DefValue correctly  :-(
++		//DefValue: goflag.DefValue,
++		DefValue: goflag.Value.String(),
++	}
++	// Ex: if the golang flag was -v, allow both -v and --v to work
++	if len(flag.Name) == 1 {
++		flag.Shorthand = flag.Name
++	}
++	if fv, ok := goflag.Value.(goBoolFlag); ok && fv.IsBoolFlag() {
++		flag.NoOptDefVal = "true"
++	}
++	return flag
++}
++
++// AddGoFlag will add the given *flag.Flag to the pflag.FlagSet
++func (f *FlagSet) AddGoFlag(goflag *goflag.Flag) {
++	if f.Lookup(goflag.Name) != nil {
++		return
++	}
++	newflag := PFlagFromGoFlag(goflag)
++	f.AddFlag(newflag)
++}
++
++// AddGoFlagSet will add the given *flag.FlagSet to the pflag.FlagSet
++func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet) {
++	if newSet == nil {
++		return
++	}
++	newSet.VisitAll(func(goflag *goflag.Flag) {
++		f.AddGoFlag(goflag)
++	})
++	if f.addedGoFlagSets == nil {
++		f.addedGoFlagSets = make([]*goflag.FlagSet, 0)
++	}
++	f.addedGoFlagSets = append(f.addedGoFlagSets, newSet)
++}
+diff --git a/thirdparty/pflag/int.go b/thirdparty/pflag/int.go
+new file mode 100644
+index 00000000..1474b89d
+--- /dev/null
++++ b/thirdparty/pflag/int.go
+@@ -0,0 +1,84 @@
++package pflag
++
++import "strconv"
++
++// -- int Value
++type intValue int
++
++func newIntValue(val int, p *int) *intValue {
++	*p = val
++	return (*intValue)(p)
++}
++
++func (i *intValue) Set(s string) error {
++	v, err := strconv.ParseInt(s, 0, 64)
++	*i = intValue(v)
++	return err
++}
++
++func (i *intValue) Type() string {
++	return "int"
++}
++
++func (i *intValue) String() string { return strconv.Itoa(int(*i)) }
++
++func intConv(sval string) (interface{}, error) {
++	return strconv.Atoi(sval)
++}
++
++// GetInt return the int value of a flag with the given name
++func (f *FlagSet) GetInt(name string) (int, error) {
++	val, err := f.getFlagType(name, "int", intConv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(int), nil
++}
++
++// IntVar defines an int flag with specified name, default value, and usage string.
++// The argument p points to an int variable in which to store the value of the flag.
++func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
++	f.VarP(newIntValue(value, p), name, "", usage)
++}
++
++// IntVarP is like IntVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IntVarP(p *int, name, shorthand string, value int, usage string) {
++	f.VarP(newIntValue(value, p), name, shorthand, usage)
++}
++
++// IntVar defines an int flag with specified name, default value, and usage string.
++// The argument p points to an int variable in which to store the value of the flag.
++func IntVar(p *int, name string, value int, usage string) {
++	CommandLine.VarP(newIntValue(value, p), name, "", usage)
++}
++
++// IntVarP is like IntVar, but accepts a shorthand letter that can be used after a single dash.
++func IntVarP(p *int, name, shorthand string, value int, usage string) {
++	CommandLine.VarP(newIntValue(value, p), name, shorthand, usage)
++}
++
++// Int defines an int flag with specified name, default value, and usage string.
++// The return value is the address of an int variable that stores the value of the flag.
++func (f *FlagSet) Int(name string, value int, usage string) *int {
++	p := new(int)
++	f.IntVarP(p, name, "", value, usage)
++	return p
++}
++
++// IntP is like Int, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IntP(name, shorthand string, value int, usage string) *int {
++	p := new(int)
++	f.IntVarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Int defines an int flag with specified name, default value, and usage string.
++// The return value is the address of an int variable that stores the value of the flag.
++func Int(name string, value int, usage string) *int {
++	return CommandLine.IntP(name, "", value, usage)
++}
++
++// IntP is like Int, but accepts a shorthand letter that can be used after a single dash.
++func IntP(name, shorthand string, value int, usage string) *int {
++	return CommandLine.IntP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/int16.go b/thirdparty/pflag/int16.go
+new file mode 100644
+index 00000000..f1a01d05
+--- /dev/null
++++ b/thirdparty/pflag/int16.go
+@@ -0,0 +1,88 @@
++package pflag
++
++import "strconv"
++
++// -- int16 Value
++type int16Value int16
++
++func newInt16Value(val int16, p *int16) *int16Value {
++	*p = val
++	return (*int16Value)(p)
++}
++
++func (i *int16Value) Set(s string) error {
++	v, err := strconv.ParseInt(s, 0, 16)
++	*i = int16Value(v)
++	return err
++}
++
++func (i *int16Value) Type() string {
++	return "int16"
++}
++
++func (i *int16Value) String() string { return strconv.FormatInt(int64(*i), 10) }
++
++func int16Conv(sval string) (interface{}, error) {
++	v, err := strconv.ParseInt(sval, 0, 16)
++	if err != nil {
++		return 0, err
++	}
++	return int16(v), nil
++}
++
++// GetInt16 returns the int16 value of a flag with the given name
++func (f *FlagSet) GetInt16(name string) (int16, error) {
++	val, err := f.getFlagType(name, "int16", int16Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(int16), nil
++}
++
++// Int16Var defines an int16 flag with specified name, default value, and usage string.
++// The argument p points to an int16 variable in which to store the value of the flag.
++func (f *FlagSet) Int16Var(p *int16, name string, value int16, usage string) {
++	f.VarP(newInt16Value(value, p), name, "", usage)
++}
++
++// Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Int16VarP(p *int16, name, shorthand string, value int16, usage string) {
++	f.VarP(newInt16Value(value, p), name, shorthand, usage)
++}
++
++// Int16Var defines an int16 flag with specified name, default value, and usage string.
++// The argument p points to an int16 variable in which to store the value of the flag.
++func Int16Var(p *int16, name string, value int16, usage string) {
++	CommandLine.VarP(newInt16Value(value, p), name, "", usage)
++}
++
++// Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash.
++func Int16VarP(p *int16, name, shorthand string, value int16, usage string) {
++	CommandLine.VarP(newInt16Value(value, p), name, shorthand, usage)
++}
++
++// Int16 defines an int16 flag with specified name, default value, and usage string.
++// The return value is the address of an int16 variable that stores the value of the flag.
++func (f *FlagSet) Int16(name string, value int16, usage string) *int16 {
++	p := new(int16)
++	f.Int16VarP(p, name, "", value, usage)
++	return p
++}
++
++// Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Int16P(name, shorthand string, value int16, usage string) *int16 {
++	p := new(int16)
++	f.Int16VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Int16 defines an int16 flag with specified name, default value, and usage string.
++// The return value is the address of an int16 variable that stores the value of the flag.
++func Int16(name string, value int16, usage string) *int16 {
++	return CommandLine.Int16P(name, "", value, usage)
++}
++
++// Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash.
++func Int16P(name, shorthand string, value int16, usage string) *int16 {
++	return CommandLine.Int16P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/int32.go b/thirdparty/pflag/int32.go
+new file mode 100644
+index 00000000..9b95944f
+--- /dev/null
++++ b/thirdparty/pflag/int32.go
+@@ -0,0 +1,88 @@
++package pflag
++
++import "strconv"
++
++// -- int32 Value
++type int32Value int32
++
++func newInt32Value(val int32, p *int32) *int32Value {
++	*p = val
++	return (*int32Value)(p)
++}
++
++func (i *int32Value) Set(s string) error {
++	v, err := strconv.ParseInt(s, 0, 32)
++	*i = int32Value(v)
++	return err
++}
++
++func (i *int32Value) Type() string {
++	return "int32"
++}
++
++func (i *int32Value) String() string { return strconv.FormatInt(int64(*i), 10) }
++
++func int32Conv(sval string) (interface{}, error) {
++	v, err := strconv.ParseInt(sval, 0, 32)
++	if err != nil {
++		return 0, err
++	}
++	return int32(v), nil
++}
++
++// GetInt32 return the int32 value of a flag with the given name
++func (f *FlagSet) GetInt32(name string) (int32, error) {
++	val, err := f.getFlagType(name, "int32", int32Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(int32), nil
++}
++
++// Int32Var defines an int32 flag with specified name, default value, and usage string.
++// The argument p points to an int32 variable in which to store the value of the flag.
++func (f *FlagSet) Int32Var(p *int32, name string, value int32, usage string) {
++	f.VarP(newInt32Value(value, p), name, "", usage)
++}
++
++// Int32VarP is like Int32Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Int32VarP(p *int32, name, shorthand string, value int32, usage string) {
++	f.VarP(newInt32Value(value, p), name, shorthand, usage)
++}
++
++// Int32Var defines an int32 flag with specified name, default value, and usage string.
++// The argument p points to an int32 variable in which to store the value of the flag.
++func Int32Var(p *int32, name string, value int32, usage string) {
++	CommandLine.VarP(newInt32Value(value, p), name, "", usage)
++}
++
++// Int32VarP is like Int32Var, but accepts a shorthand letter that can be used after a single dash.
++func Int32VarP(p *int32, name, shorthand string, value int32, usage string) {
++	CommandLine.VarP(newInt32Value(value, p), name, shorthand, usage)
++}
++
++// Int32 defines an int32 flag with specified name, default value, and usage string.
++// The return value is the address of an int32 variable that stores the value of the flag.
++func (f *FlagSet) Int32(name string, value int32, usage string) *int32 {
++	p := new(int32)
++	f.Int32VarP(p, name, "", value, usage)
++	return p
++}
++
++// Int32P is like Int32, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Int32P(name, shorthand string, value int32, usage string) *int32 {
++	p := new(int32)
++	f.Int32VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Int32 defines an int32 flag with specified name, default value, and usage string.
++// The return value is the address of an int32 variable that stores the value of the flag.
++func Int32(name string, value int32, usage string) *int32 {
++	return CommandLine.Int32P(name, "", value, usage)
++}
++
++// Int32P is like Int32, but accepts a shorthand letter that can be used after a single dash.
++func Int32P(name, shorthand string, value int32, usage string) *int32 {
++	return CommandLine.Int32P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/int64.go b/thirdparty/pflag/int64.go
+new file mode 100644
+index 00000000..0026d781
+--- /dev/null
++++ b/thirdparty/pflag/int64.go
+@@ -0,0 +1,84 @@
++package pflag
++
++import "strconv"
++
++// -- int64 Value
++type int64Value int64
++
++func newInt64Value(val int64, p *int64) *int64Value {
++	*p = val
++	return (*int64Value)(p)
++}
++
++func (i *int64Value) Set(s string) error {
++	v, err := strconv.ParseInt(s, 0, 64)
++	*i = int64Value(v)
++	return err
++}
++
++func (i *int64Value) Type() string {
++	return "int64"
++}
++
++func (i *int64Value) String() string { return strconv.FormatInt(int64(*i), 10) }
++
++func int64Conv(sval string) (interface{}, error) {
++	return strconv.ParseInt(sval, 0, 64)
++}
++
++// GetInt64 return the int64 value of a flag with the given name
++func (f *FlagSet) GetInt64(name string) (int64, error) {
++	val, err := f.getFlagType(name, "int64", int64Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(int64), nil
++}
++
++// Int64Var defines an int64 flag with specified name, default value, and usage string.
++// The argument p points to an int64 variable in which to store the value of the flag.
++func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) {
++	f.VarP(newInt64Value(value, p), name, "", usage)
++}
++
++// Int64VarP is like Int64Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Int64VarP(p *int64, name, shorthand string, value int64, usage string) {
++	f.VarP(newInt64Value(value, p), name, shorthand, usage)
++}
++
++// Int64Var defines an int64 flag with specified name, default value, and usage string.
++// The argument p points to an int64 variable in which to store the value of the flag.
++func Int64Var(p *int64, name string, value int64, usage string) {
++	CommandLine.VarP(newInt64Value(value, p), name, "", usage)
++}
++
++// Int64VarP is like Int64Var, but accepts a shorthand letter that can be used after a single dash.
++func Int64VarP(p *int64, name, shorthand string, value int64, usage string) {
++	CommandLine.VarP(newInt64Value(value, p), name, shorthand, usage)
++}
++
++// Int64 defines an int64 flag with specified name, default value, and usage string.
++// The return value is the address of an int64 variable that stores the value of the flag.
++func (f *FlagSet) Int64(name string, value int64, usage string) *int64 {
++	p := new(int64)
++	f.Int64VarP(p, name, "", value, usage)
++	return p
++}
++
++// Int64P is like Int64, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Int64P(name, shorthand string, value int64, usage string) *int64 {
++	p := new(int64)
++	f.Int64VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Int64 defines an int64 flag with specified name, default value, and usage string.
++// The return value is the address of an int64 variable that stores the value of the flag.
++func Int64(name string, value int64, usage string) *int64 {
++	return CommandLine.Int64P(name, "", value, usage)
++}
++
++// Int64P is like Int64, but accepts a shorthand letter that can be used after a single dash.
++func Int64P(name, shorthand string, value int64, usage string) *int64 {
++	return CommandLine.Int64P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/int8.go b/thirdparty/pflag/int8.go
+new file mode 100644
+index 00000000..4da92228
+--- /dev/null
++++ b/thirdparty/pflag/int8.go
+@@ -0,0 +1,88 @@
++package pflag
++
++import "strconv"
++
++// -- int8 Value
++type int8Value int8
++
++func newInt8Value(val int8, p *int8) *int8Value {
++	*p = val
++	return (*int8Value)(p)
++}
++
++func (i *int8Value) Set(s string) error {
++	v, err := strconv.ParseInt(s, 0, 8)
++	*i = int8Value(v)
++	return err
++}
++
++func (i *int8Value) Type() string {
++	return "int8"
++}
++
++func (i *int8Value) String() string { return strconv.FormatInt(int64(*i), 10) }
++
++func int8Conv(sval string) (interface{}, error) {
++	v, err := strconv.ParseInt(sval, 0, 8)
++	if err != nil {
++		return 0, err
++	}
++	return int8(v), nil
++}
++
++// GetInt8 return the int8 value of a flag with the given name
++func (f *FlagSet) GetInt8(name string) (int8, error) {
++	val, err := f.getFlagType(name, "int8", int8Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(int8), nil
++}
++
++// Int8Var defines an int8 flag with specified name, default value, and usage string.
++// The argument p points to an int8 variable in which to store the value of the flag.
++func (f *FlagSet) Int8Var(p *int8, name string, value int8, usage string) {
++	f.VarP(newInt8Value(value, p), name, "", usage)
++}
++
++// Int8VarP is like Int8Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Int8VarP(p *int8, name, shorthand string, value int8, usage string) {
++	f.VarP(newInt8Value(value, p), name, shorthand, usage)
++}
++
++// Int8Var defines an int8 flag with specified name, default value, and usage string.
++// The argument p points to an int8 variable in which to store the value of the flag.
++func Int8Var(p *int8, name string, value int8, usage string) {
++	CommandLine.VarP(newInt8Value(value, p), name, "", usage)
++}
++
++// Int8VarP is like Int8Var, but accepts a shorthand letter that can be used after a single dash.
++func Int8VarP(p *int8, name, shorthand string, value int8, usage string) {
++	CommandLine.VarP(newInt8Value(value, p), name, shorthand, usage)
++}
++
++// Int8 defines an int8 flag with specified name, default value, and usage string.
++// The return value is the address of an int8 variable that stores the value of the flag.
++func (f *FlagSet) Int8(name string, value int8, usage string) *int8 {
++	p := new(int8)
++	f.Int8VarP(p, name, "", value, usage)
++	return p
++}
++
++// Int8P is like Int8, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Int8P(name, shorthand string, value int8, usage string) *int8 {
++	p := new(int8)
++	f.Int8VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Int8 defines an int8 flag with specified name, default value, and usage string.
++// The return value is the address of an int8 variable that stores the value of the flag.
++func Int8(name string, value int8, usage string) *int8 {
++	return CommandLine.Int8P(name, "", value, usage)
++}
++
++// Int8P is like Int8, but accepts a shorthand letter that can be used after a single dash.
++func Int8P(name, shorthand string, value int8, usage string) *int8 {
++	return CommandLine.Int8P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/int_slice.go b/thirdparty/pflag/int_slice.go
+new file mode 100644
+index 00000000..1e7c9edd
+--- /dev/null
++++ b/thirdparty/pflag/int_slice.go
+@@ -0,0 +1,128 @@
++package pflag
++
++import (
++	"fmt"
++	"strconv"
++	"strings"
++)
++
++// -- intSlice Value
++type intSliceValue struct {
++	value   *[]int
++	changed bool
++}
++
++func newIntSliceValue(val []int, p *[]int) *intSliceValue {
++	isv := new(intSliceValue)
++	isv.value = p
++	*isv.value = val
++	return isv
++}
++
++func (s *intSliceValue) Set(val string) error {
++	ss := strings.Split(val, ",")
++	out := make([]int, len(ss))
++	for i, d := range ss {
++		var err error
++		out[i], err = strconv.Atoi(d)
++		if err != nil {
++			return err
++		}
++
++	}
++	if !s.changed {
++		*s.value = out
++	} else {
++		*s.value = append(*s.value, out...)
++	}
++	s.changed = true
++	return nil
++}
++
++func (s *intSliceValue) Type() string {
++	return "intSlice"
++}
++
++func (s *intSliceValue) String() string {
++	out := make([]string, len(*s.value))
++	for i, d := range *s.value {
++		out[i] = fmt.Sprintf("%d", d)
++	}
++	return "[" + strings.Join(out, ",") + "]"
++}
++
++func intSliceConv(val string) (interface{}, error) {
++	val = strings.Trim(val, "[]")
++	// Empty string would cause a slice with one (empty) entry
++	if len(val) == 0 {
++		return []int{}, nil
++	}
++	ss := strings.Split(val, ",")
++	out := make([]int, len(ss))
++	for i, d := range ss {
++		var err error
++		out[i], err = strconv.Atoi(d)
++		if err != nil {
++			return nil, err
++		}
++
++	}
++	return out, nil
++}
++
++// GetIntSlice return the []int value of a flag with the given name
++func (f *FlagSet) GetIntSlice(name string) ([]int, error) {
++	val, err := f.getFlagType(name, "intSlice", intSliceConv)
++	if err != nil {
++		return []int{}, err
++	}
++	return val.([]int), nil
++}
++
++// IntSliceVar defines a intSlice flag with specified name, default value, and usage string.
++// The argument p points to a []int variable in which to store the value of the flag.
++func (f *FlagSet) IntSliceVar(p *[]int, name string, value []int, usage string) {
++	f.VarP(newIntSliceValue(value, p), name, "", usage)
++}
++
++// IntSliceVarP is like IntSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IntSliceVarP(p *[]int, name, shorthand string, value []int, usage string) {
++	f.VarP(newIntSliceValue(value, p), name, shorthand, usage)
++}
++
++// IntSliceVar defines a int[] flag with specified name, default value, and usage string.
++// The argument p points to a int[] variable in which to store the value of the flag.
++func IntSliceVar(p *[]int, name string, value []int, usage string) {
++	CommandLine.VarP(newIntSliceValue(value, p), name, "", usage)
++}
++
++// IntSliceVarP is like IntSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func IntSliceVarP(p *[]int, name, shorthand string, value []int, usage string) {
++	CommandLine.VarP(newIntSliceValue(value, p), name, shorthand, usage)
++}
++
++// IntSlice defines a []int flag with specified name, default value, and usage string.
++// The return value is the address of a []int variable that stores the value of the flag.
++func (f *FlagSet) IntSlice(name string, value []int, usage string) *[]int {
++	p := []int{}
++	f.IntSliceVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// IntSliceP is like IntSlice, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IntSliceP(name, shorthand string, value []int, usage string) *[]int {
++	p := []int{}
++	f.IntSliceVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// IntSlice defines a []int flag with specified name, default value, and usage string.
++// The return value is the address of a []int variable that stores the value of the flag.
++func IntSlice(name string, value []int, usage string) *[]int {
++	return CommandLine.IntSliceP(name, "", value, usage)
++}
++
++// IntSliceP is like IntSlice, but accepts a shorthand letter that can be used after a single dash.
++func IntSliceP(name, shorthand string, value []int, usage string) *[]int {
++	return CommandLine.IntSliceP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/ip.go b/thirdparty/pflag/ip.go
+new file mode 100644
+index 00000000..3d414ba6
+--- /dev/null
++++ b/thirdparty/pflag/ip.go
+@@ -0,0 +1,94 @@
++package pflag
++
++import (
++	"fmt"
++	"net"
++	"strings"
++)
++
++// -- net.IP value
++type ipValue net.IP
++
++func newIPValue(val net.IP, p *net.IP) *ipValue {
++	*p = val
++	return (*ipValue)(p)
++}
++
++func (i *ipValue) String() string { return net.IP(*i).String() }
++func (i *ipValue) Set(s string) error {
++	ip := net.ParseIP(strings.TrimSpace(s))
++	if ip == nil {
++		return fmt.Errorf("failed to parse IP: %q", s)
++	}
++	*i = ipValue(ip)
++	return nil
++}
++
++func (i *ipValue) Type() string {
++	return "ip"
++}
++
++func ipConv(sval string) (interface{}, error) {
++	ip := net.ParseIP(sval)
++	if ip != nil {
++		return ip, nil
++	}
++	return nil, fmt.Errorf("invalid string being converted to IP address: %s", sval)
++}
++
++// GetIP return the net.IP value of a flag with the given name
++func (f *FlagSet) GetIP(name string) (net.IP, error) {
++	val, err := f.getFlagType(name, "ip", ipConv)
++	if err != nil {
++		return nil, err
++	}
++	return val.(net.IP), nil
++}
++
++// IPVar defines an net.IP flag with specified name, default value, and usage string.
++// The argument p points to an net.IP variable in which to store the value of the flag.
++func (f *FlagSet) IPVar(p *net.IP, name string, value net.IP, usage string) {
++	f.VarP(newIPValue(value, p), name, "", usage)
++}
++
++// IPVarP is like IPVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPVarP(p *net.IP, name, shorthand string, value net.IP, usage string) {
++	f.VarP(newIPValue(value, p), name, shorthand, usage)
++}
++
++// IPVar defines an net.IP flag with specified name, default value, and usage string.
++// The argument p points to an net.IP variable in which to store the value of the flag.
++func IPVar(p *net.IP, name string, value net.IP, usage string) {
++	CommandLine.VarP(newIPValue(value, p), name, "", usage)
++}
++
++// IPVarP is like IPVar, but accepts a shorthand letter that can be used after a single dash.
++func IPVarP(p *net.IP, name, shorthand string, value net.IP, usage string) {
++	CommandLine.VarP(newIPValue(value, p), name, shorthand, usage)
++}
++
++// IP defines an net.IP flag with specified name, default value, and usage string.
++// The return value is the address of an net.IP variable that stores the value of the flag.
++func (f *FlagSet) IP(name string, value net.IP, usage string) *net.IP {
++	p := new(net.IP)
++	f.IPVarP(p, name, "", value, usage)
++	return p
++}
++
++// IPP is like IP, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPP(name, shorthand string, value net.IP, usage string) *net.IP {
++	p := new(net.IP)
++	f.IPVarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// IP defines an net.IP flag with specified name, default value, and usage string.
++// The return value is the address of an net.IP variable that stores the value of the flag.
++func IP(name string, value net.IP, usage string) *net.IP {
++	return CommandLine.IPP(name, "", value, usage)
++}
++
++// IPP is like IP, but accepts a shorthand letter that can be used after a single dash.
++func IPP(name, shorthand string, value net.IP, usage string) *net.IP {
++	return CommandLine.IPP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/ip_slice.go b/thirdparty/pflag/ip_slice.go
+new file mode 100644
+index 00000000..7dd196fe
+--- /dev/null
++++ b/thirdparty/pflag/ip_slice.go
+@@ -0,0 +1,148 @@
++package pflag
++
++import (
++	"fmt"
++	"io"
++	"net"
++	"strings"
++)
++
++// -- ipSlice Value
++type ipSliceValue struct {
++	value   *[]net.IP
++	changed bool
++}
++
++func newIPSliceValue(val []net.IP, p *[]net.IP) *ipSliceValue {
++	ipsv := new(ipSliceValue)
++	ipsv.value = p
++	*ipsv.value = val
++	return ipsv
++}
++
++// Set converts, and assigns, the comma-separated IP argument string representation as the []net.IP value of this flag.
++// If Set is called on a flag that already has a []net.IP assigned, the newly converted values will be appended.
++func (s *ipSliceValue) Set(val string) error {
++
++	// remove all quote characters
++	rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "")
++
++	// read flag arguments with CSV parser
++	ipStrSlice, err := readAsCSV(rmQuote.Replace(val))
++	if err != nil && err != io.EOF {
++		return err
++	}
++
++	// parse ip values into slice
++	out := make([]net.IP, 0, len(ipStrSlice))
++	for _, ipStr := range ipStrSlice {
++		ip := net.ParseIP(strings.TrimSpace(ipStr))
++		if ip == nil {
++			return fmt.Errorf("invalid string being converted to IP address: %s", ipStr)
++		}
++		out = append(out, ip)
++	}
++
++	if !s.changed {
++		*s.value = out
++	} else {
++		*s.value = append(*s.value, out...)
++	}
++
++	s.changed = true
++
++	return nil
++}
++
++// Type returns a string that uniquely represents this flag's type.
++func (s *ipSliceValue) Type() string {
++	return "ipSlice"
++}
++
++// String defines a "native" format for this net.IP slice flag value.
++func (s *ipSliceValue) String() string {
++
++	ipStrSlice := make([]string, len(*s.value))
++	for i, ip := range *s.value {
++		ipStrSlice[i] = ip.String()
++	}
++
++	out, _ := writeAsCSV(ipStrSlice)
++
++	return "[" + out + "]"
++}
++
++func ipSliceConv(val string) (interface{}, error) {
++	val = strings.Trim(val, "[]")
++	// Emtpy string would cause a slice with one (empty) entry
++	if len(val) == 0 {
++		return []net.IP{}, nil
++	}
++	ss := strings.Split(val, ",")
++	out := make([]net.IP, len(ss))
++	for i, sval := range ss {
++		ip := net.ParseIP(strings.TrimSpace(sval))
++		if ip == nil {
++			return nil, fmt.Errorf("invalid string being converted to IP address: %s", sval)
++		}
++		out[i] = ip
++	}
++	return out, nil
++}
++
++// GetIPSlice returns the []net.IP value of a flag with the given name
++func (f *FlagSet) GetIPSlice(name string) ([]net.IP, error) {
++	val, err := f.getFlagType(name, "ipSlice", ipSliceConv)
++	if err != nil {
++		return []net.IP{}, err
++	}
++	return val.([]net.IP), nil
++}
++
++// IPSliceVar defines a ipSlice flag with specified name, default value, and usage string.
++// The argument p points to a []net.IP variable in which to store the value of the flag.
++func (f *FlagSet) IPSliceVar(p *[]net.IP, name string, value []net.IP, usage string) {
++	f.VarP(newIPSliceValue(value, p), name, "", usage)
++}
++
++// IPSliceVarP is like IPSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) {
++	f.VarP(newIPSliceValue(value, p), name, shorthand, usage)
++}
++
++// IPSliceVar defines a []net.IP flag with specified name, default value, and usage string.
++// The argument p points to a []net.IP variable in which to store the value of the flag.
++func IPSliceVar(p *[]net.IP, name string, value []net.IP, usage string) {
++	CommandLine.VarP(newIPSliceValue(value, p), name, "", usage)
++}
++
++// IPSliceVarP is like IPSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) {
++	CommandLine.VarP(newIPSliceValue(value, p), name, shorthand, usage)
++}
++
++// IPSlice defines a []net.IP flag with specified name, default value, and usage string.
++// The return value is the address of a []net.IP variable that stores the value of that flag.
++func (f *FlagSet) IPSlice(name string, value []net.IP, usage string) *[]net.IP {
++	p := []net.IP{}
++	f.IPSliceVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// IPSliceP is like IPSlice, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPSliceP(name, shorthand string, value []net.IP, usage string) *[]net.IP {
++	p := []net.IP{}
++	f.IPSliceVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// IPSlice defines a []net.IP flag with specified name, default value, and usage string.
++// The return value is the address of a []net.IP variable that stores the value of the flag.
++func IPSlice(name string, value []net.IP, usage string) *[]net.IP {
++	return CommandLine.IPSliceP(name, "", value, usage)
++}
++
++// IPSliceP is like IPSlice, but accepts a shorthand letter that can be used after a single dash.
++func IPSliceP(name, shorthand string, value []net.IP, usage string) *[]net.IP {
++	return CommandLine.IPSliceP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/ipmask.go b/thirdparty/pflag/ipmask.go
+new file mode 100644
+index 00000000..5bd44bd2
+--- /dev/null
++++ b/thirdparty/pflag/ipmask.go
+@@ -0,0 +1,122 @@
++package pflag
++
++import (
++	"fmt"
++	"net"
++	"strconv"
++)
++
++// -- net.IPMask value
++type ipMaskValue net.IPMask
++
++func newIPMaskValue(val net.IPMask, p *net.IPMask) *ipMaskValue {
++	*p = val
++	return (*ipMaskValue)(p)
++}
++
++func (i *ipMaskValue) String() string { return net.IPMask(*i).String() }
++func (i *ipMaskValue) Set(s string) error {
++	ip := ParseIPv4Mask(s)
++	if ip == nil {
++		return fmt.Errorf("failed to parse IP mask: %q", s)
++	}
++	*i = ipMaskValue(ip)
++	return nil
++}
++
++func (i *ipMaskValue) Type() string {
++	return "ipMask"
++}
++
++// ParseIPv4Mask written in IP form (e.g. 255.255.255.0).
++// This function should really belong to the net package.
++func ParseIPv4Mask(s string) net.IPMask {
++	mask := net.ParseIP(s)
++	if mask == nil {
++		if len(s) != 8 {
++			return nil
++		}
++		// net.IPMask.String() actually outputs things like ffffff00
++		// so write a horrible parser for that as well  :-(
++		m := []int{}
++		for i := 0; i < 4; i++ {
++			b := "0x" + s[2*i:2*i+2]
++			d, err := strconv.ParseInt(b, 0, 0)
++			if err != nil {
++				return nil
++			}
++			m = append(m, int(d))
++		}
++		s := fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3])
++		mask = net.ParseIP(s)
++		if mask == nil {
++			return nil
++		}
++	}
++	return net.IPv4Mask(mask[12], mask[13], mask[14], mask[15])
++}
++
++func parseIPv4Mask(sval string) (interface{}, error) {
++	mask := ParseIPv4Mask(sval)
++	if mask == nil {
++		return nil, fmt.Errorf("unable to parse %s as net.IPMask", sval)
++	}
++	return mask, nil
++}
++
++// GetIPv4Mask return the net.IPv4Mask value of a flag with the given name
++func (f *FlagSet) GetIPv4Mask(name string) (net.IPMask, error) {
++	val, err := f.getFlagType(name, "ipMask", parseIPv4Mask)
++	if err != nil {
++		return nil, err
++	}
++	return val.(net.IPMask), nil
++}
++
++// IPMaskVar defines an net.IPMask flag with specified name, default value, and usage string.
++// The argument p points to an net.IPMask variable in which to store the value of the flag.
++func (f *FlagSet) IPMaskVar(p *net.IPMask, name string, value net.IPMask, usage string) {
++	f.VarP(newIPMaskValue(value, p), name, "", usage)
++}
++
++// IPMaskVarP is like IPMaskVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPMaskVarP(p *net.IPMask, name, shorthand string, value net.IPMask, usage string) {
++	f.VarP(newIPMaskValue(value, p), name, shorthand, usage)
++}
++
++// IPMaskVar defines an net.IPMask flag with specified name, default value, and usage string.
++// The argument p points to an net.IPMask variable in which to store the value of the flag.
++func IPMaskVar(p *net.IPMask, name string, value net.IPMask, usage string) {
++	CommandLine.VarP(newIPMaskValue(value, p), name, "", usage)
++}
++
++// IPMaskVarP is like IPMaskVar, but accepts a shorthand letter that can be used after a single dash.
++func IPMaskVarP(p *net.IPMask, name, shorthand string, value net.IPMask, usage string) {
++	CommandLine.VarP(newIPMaskValue(value, p), name, shorthand, usage)
++}
++
++// IPMask defines an net.IPMask flag with specified name, default value, and usage string.
++// The return value is the address of an net.IPMask variable that stores the value of the flag.
++func (f *FlagSet) IPMask(name string, value net.IPMask, usage string) *net.IPMask {
++	p := new(net.IPMask)
++	f.IPMaskVarP(p, name, "", value, usage)
++	return p
++}
++
++// IPMaskP is like IPMask, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPMaskP(name, shorthand string, value net.IPMask, usage string) *net.IPMask {
++	p := new(net.IPMask)
++	f.IPMaskVarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// IPMask defines an net.IPMask flag with specified name, default value, and usage string.
++// The return value is the address of an net.IPMask variable that stores the value of the flag.
++func IPMask(name string, value net.IPMask, usage string) *net.IPMask {
++	return CommandLine.IPMaskP(name, "", value, usage)
++}
++
++// IPMaskP is like IP, but accepts a shorthand letter that can be used after a single dash.
++func IPMaskP(name, shorthand string, value net.IPMask, usage string) *net.IPMask {
++	return CommandLine.IPMaskP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/ipnet.go b/thirdparty/pflag/ipnet.go
+new file mode 100644
+index 00000000..e2c1b8bc
+--- /dev/null
++++ b/thirdparty/pflag/ipnet.go
+@@ -0,0 +1,98 @@
++package pflag
++
++import (
++	"fmt"
++	"net"
++	"strings"
++)
++
++// IPNet adapts net.IPNet for use as a flag.
++type ipNetValue net.IPNet
++
++func (ipnet ipNetValue) String() string {
++	n := net.IPNet(ipnet)
++	return n.String()
++}
++
++func (ipnet *ipNetValue) Set(value string) error {
++	_, n, err := net.ParseCIDR(strings.TrimSpace(value))
++	if err != nil {
++		return err
++	}
++	*ipnet = ipNetValue(*n)
++	return nil
++}
++
++func (*ipNetValue) Type() string {
++	return "ipNet"
++}
++
++func newIPNetValue(val net.IPNet, p *net.IPNet) *ipNetValue {
++	*p = val
++	return (*ipNetValue)(p)
++}
++
++func ipNetConv(sval string) (interface{}, error) {
++	_, n, err := net.ParseCIDR(strings.TrimSpace(sval))
++	if err == nil {
++		return *n, nil
++	}
++	return nil, fmt.Errorf("invalid string being converted to IPNet: %s", sval)
++}
++
++// GetIPNet return the net.IPNet value of a flag with the given name
++func (f *FlagSet) GetIPNet(name string) (net.IPNet, error) {
++	val, err := f.getFlagType(name, "ipNet", ipNetConv)
++	if err != nil {
++		return net.IPNet{}, err
++	}
++	return val.(net.IPNet), nil
++}
++
++// IPNetVar defines an net.IPNet flag with specified name, default value, and usage string.
++// The argument p points to an net.IPNet variable in which to store the value of the flag.
++func (f *FlagSet) IPNetVar(p *net.IPNet, name string, value net.IPNet, usage string) {
++	f.VarP(newIPNetValue(value, p), name, "", usage)
++}
++
++// IPNetVarP is like IPNetVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPNetVarP(p *net.IPNet, name, shorthand string, value net.IPNet, usage string) {
++	f.VarP(newIPNetValue(value, p), name, shorthand, usage)
++}
++
++// IPNetVar defines an net.IPNet flag with specified name, default value, and usage string.
++// The argument p points to an net.IPNet variable in which to store the value of the flag.
++func IPNetVar(p *net.IPNet, name string, value net.IPNet, usage string) {
++	CommandLine.VarP(newIPNetValue(value, p), name, "", usage)
++}
++
++// IPNetVarP is like IPNetVar, but accepts a shorthand letter that can be used after a single dash.
++func IPNetVarP(p *net.IPNet, name, shorthand string, value net.IPNet, usage string) {
++	CommandLine.VarP(newIPNetValue(value, p), name, shorthand, usage)
++}
++
++// IPNet defines an net.IPNet flag with specified name, default value, and usage string.
++// The return value is the address of an net.IPNet variable that stores the value of the flag.
++func (f *FlagSet) IPNet(name string, value net.IPNet, usage string) *net.IPNet {
++	p := new(net.IPNet)
++	f.IPNetVarP(p, name, "", value, usage)
++	return p
++}
++
++// IPNetP is like IPNet, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPNetP(name, shorthand string, value net.IPNet, usage string) *net.IPNet {
++	p := new(net.IPNet)
++	f.IPNetVarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// IPNet defines an net.IPNet flag with specified name, default value, and usage string.
++// The return value is the address of an net.IPNet variable that stores the value of the flag.
++func IPNet(name string, value net.IPNet, usage string) *net.IPNet {
++	return CommandLine.IPNetP(name, "", value, usage)
++}
++
++// IPNetP is like IPNet, but accepts a shorthand letter that can be used after a single dash.
++func IPNetP(name, shorthand string, value net.IPNet, usage string) *net.IPNet {
++	return CommandLine.IPNetP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/ipnet_slice.go b/thirdparty/pflag/ipnet_slice.go
+new file mode 100644
+index 00000000..6b541aa8
+--- /dev/null
++++ b/thirdparty/pflag/ipnet_slice.go
+@@ -0,0 +1,147 @@
++package pflag
++
++import (
++	"fmt"
++	"io"
++	"net"
++	"strings"
++)
++
++// -- ipNetSlice Value
++type ipNetSliceValue struct {
++	value   *[]net.IPNet
++	changed bool
++}
++
++func newIPNetSliceValue(val []net.IPNet, p *[]net.IPNet) *ipNetSliceValue {
++	ipnsv := new(ipNetSliceValue)
++	ipnsv.value = p
++	*ipnsv.value = val
++	return ipnsv
++}
++
++// Set converts, and assigns, the comma-separated IPNet argument string representation as the []net.IPNet value of this flag.
++// If Set is called on a flag that already has a []net.IPNet assigned, the newly converted values will be appended.
++func (s *ipNetSliceValue) Set(val string) error {
++
++	// remove all quote characters
++	rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "")
++
++	// read flag arguments with CSV parser
++	ipNetStrSlice, err := readAsCSV(rmQuote.Replace(val))
++	if err != nil && err != io.EOF {
++		return err
++	}
++
++	// parse ip values into slice
++	out := make([]net.IPNet, 0, len(ipNetStrSlice))
++	for _, ipNetStr := range ipNetStrSlice {
++		_, n, err := net.ParseCIDR(strings.TrimSpace(ipNetStr))
++		if err != nil {
++			return fmt.Errorf("invalid string being converted to CIDR: %s", ipNetStr)
++		}
++		out = append(out, *n)
++	}
++
++	if !s.changed {
++		*s.value = out
++	} else {
++		*s.value = append(*s.value, out...)
++	}
++
++	s.changed = true
++
++	return nil
++}
++
++// Type returns a string that uniquely represents this flag's type.
++func (s *ipNetSliceValue) Type() string {
++	return "ipNetSlice"
++}
++
++// String defines a "native" format for this net.IPNet slice flag value.
++func (s *ipNetSliceValue) String() string {
++
++	ipNetStrSlice := make([]string, len(*s.value))
++	for i, n := range *s.value {
++		ipNetStrSlice[i] = n.String()
++	}
++
++	out, _ := writeAsCSV(ipNetStrSlice)
++	return "[" + out + "]"
++}
++
++func ipNetSliceConv(val string) (interface{}, error) {
++	val = strings.Trim(val, "[]")
++	// Emtpy string would cause a slice with one (empty) entry
++	if len(val) == 0 {
++		return []net.IPNet{}, nil
++	}
++	ss := strings.Split(val, ",")
++	out := make([]net.IPNet, len(ss))
++	for i, sval := range ss {
++		_, n, err := net.ParseCIDR(strings.TrimSpace(sval))
++		if err != nil {
++			return nil, fmt.Errorf("invalid string being converted to CIDR: %s", sval)
++		}
++		out[i] = *n
++	}
++	return out, nil
++}
++
++// GetIPNetSlice returns the []net.IPNet value of a flag with the given name
++func (f *FlagSet) GetIPNetSlice(name string) ([]net.IPNet, error) {
++	val, err := f.getFlagType(name, "ipNetSlice", ipNetSliceConv)
++	if err != nil {
++		return []net.IPNet{}, err
++	}
++	return val.([]net.IPNet), nil
++}
++
++// IPNetSliceVar defines a ipNetSlice flag with specified name, default value, and usage string.
++// The argument p points to a []net.IPNet variable in which to store the value of the flag.
++func (f *FlagSet) IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) {
++	f.VarP(newIPNetSliceValue(value, p), name, "", usage)
++}
++
++// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) {
++	f.VarP(newIPNetSliceValue(value, p), name, shorthand, usage)
++}
++
++// IPNetSliceVar defines a []net.IPNet flag with specified name, default value, and usage string.
++// The argument p points to a []net.IPNet variable in which to store the value of the flag.
++func IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) {
++	CommandLine.VarP(newIPNetSliceValue(value, p), name, "", usage)
++}
++
++// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) {
++	CommandLine.VarP(newIPNetSliceValue(value, p), name, shorthand, usage)
++}
++
++// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string.
++// The return value is the address of a []net.IPNet variable that stores the value of that flag.
++func (f *FlagSet) IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet {
++	p := []net.IPNet{}
++	f.IPNetSliceVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet {
++	p := []net.IPNet{}
++	f.IPNetSliceVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string.
++// The return value is the address of a []net.IP variable that stores the value of the flag.
++func IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet {
++	return CommandLine.IPNetSliceP(name, "", value, usage)
++}
++
++// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash.
++func IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet {
++	return CommandLine.IPNetSliceP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/string.go b/thirdparty/pflag/string.go
+new file mode 100644
+index 00000000..04e0a26f
+--- /dev/null
++++ b/thirdparty/pflag/string.go
+@@ -0,0 +1,80 @@
++package pflag
++
++// -- string Value
++type stringValue string
++
++func newStringValue(val string, p *string) *stringValue {
++	*p = val
++	return (*stringValue)(p)
++}
++
++func (s *stringValue) Set(val string) error {
++	*s = stringValue(val)
++	return nil
++}
++func (s *stringValue) Type() string {
++	return "string"
++}
++
++func (s *stringValue) String() string { return string(*s) }
++
++func stringConv(sval string) (interface{}, error) {
++	return sval, nil
++}
++
++// GetString return the string value of a flag with the given name
++func (f *FlagSet) GetString(name string) (string, error) {
++	val, err := f.getFlagType(name, "string", stringConv)
++	if err != nil {
++		return "", err
++	}
++	return val.(string), nil
++}
++
++// StringVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a string variable in which to store the value of the flag.
++func (f *FlagSet) StringVar(p *string, name string, value string, usage string) {
++	f.VarP(newStringValue(value, p), name, "", usage)
++}
++
++// StringVarP is like StringVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringVarP(p *string, name, shorthand string, value string, usage string) {
++	f.VarP(newStringValue(value, p), name, shorthand, usage)
++}
++
++// StringVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a string variable in which to store the value of the flag.
++func StringVar(p *string, name string, value string, usage string) {
++	CommandLine.VarP(newStringValue(value, p), name, "", usage)
++}
++
++// StringVarP is like StringVar, but accepts a shorthand letter that can be used after a single dash.
++func StringVarP(p *string, name, shorthand string, value string, usage string) {
++	CommandLine.VarP(newStringValue(value, p), name, shorthand, usage)
++}
++
++// String defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a string variable that stores the value of the flag.
++func (f *FlagSet) String(name string, value string, usage string) *string {
++	p := new(string)
++	f.StringVarP(p, name, "", value, usage)
++	return p
++}
++
++// StringP is like String, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringP(name, shorthand string, value string, usage string) *string {
++	p := new(string)
++	f.StringVarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// String defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a string variable that stores the value of the flag.
++func String(name string, value string, usage string) *string {
++	return CommandLine.StringP(name, "", value, usage)
++}
++
++// StringP is like String, but accepts a shorthand letter that can be used after a single dash.
++func StringP(name, shorthand string, value string, usage string) *string {
++	return CommandLine.StringP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/string_array.go b/thirdparty/pflag/string_array.go
+new file mode 100644
+index 00000000..fa7bc601
+--- /dev/null
++++ b/thirdparty/pflag/string_array.go
+@@ -0,0 +1,103 @@
++package pflag
++
++// -- stringArray Value
++type stringArrayValue struct {
++	value   *[]string
++	changed bool
++}
++
++func newStringArrayValue(val []string, p *[]string) *stringArrayValue {
++	ssv := new(stringArrayValue)
++	ssv.value = p
++	*ssv.value = val
++	return ssv
++}
++
++func (s *stringArrayValue) Set(val string) error {
++	if !s.changed {
++		*s.value = []string{val}
++		s.changed = true
++	} else {
++		*s.value = append(*s.value, val)
++	}
++	return nil
++}
++
++func (s *stringArrayValue) Type() string {
++	return "stringArray"
++}
++
++func (s *stringArrayValue) String() string {
++	str, _ := writeAsCSV(*s.value)
++	return "[" + str + "]"
++}
++
++func stringArrayConv(sval string) (interface{}, error) {
++	sval = sval[1 : len(sval)-1]
++	// An empty string would cause a array with one (empty) string
++	if len(sval) == 0 {
++		return []string{}, nil
++	}
++	return readAsCSV(sval)
++}
++
++// GetStringArray return the []string value of a flag with the given name
++func (f *FlagSet) GetStringArray(name string) ([]string, error) {
++	val, err := f.getFlagType(name, "stringArray", stringArrayConv)
++	if err != nil {
++		return []string{}, err
++	}
++	return val.([]string), nil
++}
++
++// StringArrayVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a []string variable in which to store the values of the multiple flags.
++// The value of each argument will not try to be separated by comma. Use a StringSlice for that.
++func (f *FlagSet) StringArrayVar(p *[]string, name string, value []string, usage string) {
++	f.VarP(newStringArrayValue(value, p), name, "", usage)
++}
++
++// StringArrayVarP is like StringArrayVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringArrayVarP(p *[]string, name, shorthand string, value []string, usage string) {
++	f.VarP(newStringArrayValue(value, p), name, shorthand, usage)
++}
++
++// StringArrayVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a []string variable in which to store the value of the flag.
++// The value of each argument will not try to be separated by comma. Use a StringSlice for that.
++func StringArrayVar(p *[]string, name string, value []string, usage string) {
++	CommandLine.VarP(newStringArrayValue(value, p), name, "", usage)
++}
++
++// StringArrayVarP is like StringArrayVar, but accepts a shorthand letter that can be used after a single dash.
++func StringArrayVarP(p *[]string, name, shorthand string, value []string, usage string) {
++	CommandLine.VarP(newStringArrayValue(value, p), name, shorthand, usage)
++}
++
++// StringArray defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a []string variable that stores the value of the flag.
++// The value of each argument will not try to be separated by comma. Use a StringSlice for that.
++func (f *FlagSet) StringArray(name string, value []string, usage string) *[]string {
++	p := []string{}
++	f.StringArrayVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// StringArrayP is like StringArray, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringArrayP(name, shorthand string, value []string, usage string) *[]string {
++	p := []string{}
++	f.StringArrayVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// StringArray defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a []string variable that stores the value of the flag.
++// The value of each argument will not try to be separated by comma. Use a StringSlice for that.
++func StringArray(name string, value []string, usage string) *[]string {
++	return CommandLine.StringArrayP(name, "", value, usage)
++}
++
++// StringArrayP is like StringArray, but accepts a shorthand letter that can be used after a single dash.
++func StringArrayP(name, shorthand string, value []string, usage string) *[]string {
++	return CommandLine.StringArrayP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/string_slice.go b/thirdparty/pflag/string_slice.go
+new file mode 100644
+index 00000000..0cd3ccc0
+--- /dev/null
++++ b/thirdparty/pflag/string_slice.go
+@@ -0,0 +1,149 @@
++package pflag
++
++import (
++	"bytes"
++	"encoding/csv"
++	"strings"
++)
++
++// -- stringSlice Value
++type stringSliceValue struct {
++	value   *[]string
++	changed bool
++}
++
++func newStringSliceValue(val []string, p *[]string) *stringSliceValue {
++	ssv := new(stringSliceValue)
++	ssv.value = p
++	*ssv.value = val
++	return ssv
++}
++
++func readAsCSV(val string) ([]string, error) {
++	if val == "" {
++		return []string{}, nil
++	}
++	stringReader := strings.NewReader(val)
++	csvReader := csv.NewReader(stringReader)
++	return csvReader.Read()
++}
++
++func writeAsCSV(vals []string) (string, error) {
++	b := &bytes.Buffer{}
++	w := csv.NewWriter(b)
++	err := w.Write(vals)
++	if err != nil {
++		return "", err
++	}
++	w.Flush()
++	return strings.TrimSuffix(b.String(), "\n"), nil
++}
++
++func (s *stringSliceValue) Set(val string) error {
++	v, err := readAsCSV(val)
++	if err != nil {
++		return err
++	}
++	if !s.changed {
++		*s.value = v
++	} else {
++		*s.value = append(*s.value, v...)
++	}
++	s.changed = true
++	return nil
++}
++
++func (s *stringSliceValue) Type() string {
++	return "stringSlice"
++}
++
++func (s *stringSliceValue) String() string {
++	str, _ := writeAsCSV(*s.value)
++	return "[" + str + "]"
++}
++
++func stringSliceConv(sval string) (interface{}, error) {
++	sval = sval[1 : len(sval)-1]
++	// An empty string would cause a slice with one (empty) string
++	if len(sval) == 0 {
++		return []string{}, nil
++	}
++	return readAsCSV(sval)
++}
++
++// GetStringSlice return the []string value of a flag with the given name
++func (f *FlagSet) GetStringSlice(name string) ([]string, error) {
++	val, err := f.getFlagType(name, "stringSlice", stringSliceConv)
++	if err != nil {
++		return []string{}, err
++	}
++	return val.([]string), nil
++}
++
++// StringSliceVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a []string variable in which to store the value of the flag.
++// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
++// For example:
++//   --ss="v1,v2" -ss="v3"
++// will result in
++//   []string{"v1", "v2", "v3"}
++func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) {
++	f.VarP(newStringSliceValue(value, p), name, "", usage)
++}
++
++// StringSliceVarP is like StringSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringSliceVarP(p *[]string, name, shorthand string, value []string, usage string) {
++	f.VarP(newStringSliceValue(value, p), name, shorthand, usage)
++}
++
++// StringSliceVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a []string variable in which to store the value of the flag.
++// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
++// For example:
++//   --ss="v1,v2" -ss="v3"
++// will result in
++//   []string{"v1", "v2", "v3"}
++func StringSliceVar(p *[]string, name string, value []string, usage string) {
++	CommandLine.VarP(newStringSliceValue(value, p), name, "", usage)
++}
++
++// StringSliceVarP is like StringSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func StringSliceVarP(p *[]string, name, shorthand string, value []string, usage string) {
++	CommandLine.VarP(newStringSliceValue(value, p), name, shorthand, usage)
++}
++
++// StringSlice defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a []string variable that stores the value of the flag.
++// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
++// For example:
++//   --ss="v1,v2" -ss="v3"
++// will result in
++//   []string{"v1", "v2", "v3"}
++func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string {
++	p := []string{}
++	f.StringSliceVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// StringSliceP is like StringSlice, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringSliceP(name, shorthand string, value []string, usage string) *[]string {
++	p := []string{}
++	f.StringSliceVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// StringSlice defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a []string variable that stores the value of the flag.
++// Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly.
++// For example:
++//   --ss="v1,v2" -ss="v3"
++// will result in
++//   []string{"v1", "v2", "v3"}
++func StringSlice(name string, value []string, usage string) *[]string {
++	return CommandLine.StringSliceP(name, "", value, usage)
++}
++
++// StringSliceP is like StringSlice, but accepts a shorthand letter that can be used after a single dash.
++func StringSliceP(name, shorthand string, value []string, usage string) *[]string {
++	return CommandLine.StringSliceP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/string_to_int.go b/thirdparty/pflag/string_to_int.go
+new file mode 100644
+index 00000000..5ceda396
+--- /dev/null
++++ b/thirdparty/pflag/string_to_int.go
+@@ -0,0 +1,149 @@
++package pflag
++
++import (
++	"bytes"
++	"fmt"
++	"strconv"
++	"strings"
++)
++
++// -- stringToInt Value
++type stringToIntValue struct {
++	value   *map[string]int
++	changed bool
++}
++
++func newStringToIntValue(val map[string]int, p *map[string]int) *stringToIntValue {
++	ssv := new(stringToIntValue)
++	ssv.value = p
++	*ssv.value = val
++	return ssv
++}
++
++// Format: a=1,b=2
++func (s *stringToIntValue) Set(val string) error {
++	ss := strings.Split(val, ",")
++	out := make(map[string]int, len(ss))
++	for _, pair := range ss {
++		kv := strings.SplitN(pair, "=", 2)
++		if len(kv) != 2 {
++			return fmt.Errorf("%s must be formatted as key=value", pair)
++		}
++		var err error
++		out[kv[0]], err = strconv.Atoi(kv[1])
++		if err != nil {
++			return err
++		}
++	}
++	if !s.changed {
++		*s.value = out
++	} else {
++		for k, v := range out {
++			(*s.value)[k] = v
++		}
++	}
++	s.changed = true
++	return nil
++}
++
++func (s *stringToIntValue) Type() string {
++	return "stringToInt"
++}
++
++func (s *stringToIntValue) String() string {
++	var buf bytes.Buffer
++	i := 0
++	for k, v := range *s.value {
++		if i > 0 {
++			buf.WriteRune(',')
++		}
++		buf.WriteString(k)
++		buf.WriteRune('=')
++		buf.WriteString(strconv.Itoa(v))
++		i++
++	}
++	return "[" + buf.String() + "]"
++}
++
++func stringToIntConv(val string) (interface{}, error) {
++	val = strings.Trim(val, "[]")
++	// An empty string would cause an empty map
++	if len(val) == 0 {
++		return map[string]int{}, nil
++	}
++	ss := strings.Split(val, ",")
++	out := make(map[string]int, len(ss))
++	for _, pair := range ss {
++		kv := strings.SplitN(pair, "=", 2)
++		if len(kv) != 2 {
++			return nil, fmt.Errorf("%s must be formatted as key=value", pair)
++		}
++		var err error
++		out[kv[0]], err = strconv.Atoi(kv[1])
++		if err != nil {
++			return nil, err
++		}
++	}
++	return out, nil
++}
++
++// GetStringToInt return the map[string]int value of a flag with the given name
++func (f *FlagSet) GetStringToInt(name string) (map[string]int, error) {
++	val, err := f.getFlagType(name, "stringToInt", stringToIntConv)
++	if err != nil {
++		return map[string]int{}, err
++	}
++	return val.(map[string]int), nil
++}
++
++// StringToIntVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a map[string]int variable in which to store the values of the multiple flags.
++// The value of each argument will not try to be separated by comma
++func (f *FlagSet) StringToIntVar(p *map[string]int, name string, value map[string]int, usage string) {
++	f.VarP(newStringToIntValue(value, p), name, "", usage)
++}
++
++// StringToIntVarP is like StringToIntVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringToIntVarP(p *map[string]int, name, shorthand string, value map[string]int, usage string) {
++	f.VarP(newStringToIntValue(value, p), name, shorthand, usage)
++}
++
++// StringToIntVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a map[string]int variable in which to store the value of the flag.
++// The value of each argument will not try to be separated by comma
++func StringToIntVar(p *map[string]int, name string, value map[string]int, usage string) {
++	CommandLine.VarP(newStringToIntValue(value, p), name, "", usage)
++}
++
++// StringToIntVarP is like StringToIntVar, but accepts a shorthand letter that can be used after a single dash.
++func StringToIntVarP(p *map[string]int, name, shorthand string, value map[string]int, usage string) {
++	CommandLine.VarP(newStringToIntValue(value, p), name, shorthand, usage)
++}
++
++// StringToInt defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a map[string]int variable that stores the value of the flag.
++// The value of each argument will not try to be separated by comma
++func (f *FlagSet) StringToInt(name string, value map[string]int, usage string) *map[string]int {
++	p := map[string]int{}
++	f.StringToIntVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// StringToIntP is like StringToInt, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringToIntP(name, shorthand string, value map[string]int, usage string) *map[string]int {
++	p := map[string]int{}
++	f.StringToIntVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// StringToInt defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a map[string]int variable that stores the value of the flag.
++// The value of each argument will not try to be separated by comma
++func StringToInt(name string, value map[string]int, usage string) *map[string]int {
++	return CommandLine.StringToIntP(name, "", value, usage)
++}
++
++// StringToIntP is like StringToInt, but accepts a shorthand letter that can be used after a single dash.
++func StringToIntP(name, shorthand string, value map[string]int, usage string) *map[string]int {
++	return CommandLine.StringToIntP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/string_to_string.go b/thirdparty/pflag/string_to_string.go
+new file mode 100644
+index 00000000..890a01af
+--- /dev/null
++++ b/thirdparty/pflag/string_to_string.go
+@@ -0,0 +1,160 @@
++package pflag
++
++import (
++	"bytes"
++	"encoding/csv"
++	"fmt"
++	"strings"
++)
++
++// -- stringToString Value
++type stringToStringValue struct {
++	value   *map[string]string
++	changed bool
++}
++
++func newStringToStringValue(val map[string]string, p *map[string]string) *stringToStringValue {
++	ssv := new(stringToStringValue)
++	ssv.value = p
++	*ssv.value = val
++	return ssv
++}
++
++// Format: a=1,b=2
++func (s *stringToStringValue) Set(val string) error {
++	var ss []string
++	n := strings.Count(val, "=")
++	switch n {
++	case 0:
++		return fmt.Errorf("%s must be formatted as key=value", val)
++	case 1:
++		ss = append(ss, strings.Trim(val, `"`))
++	default:
++		r := csv.NewReader(strings.NewReader(val))
++		var err error
++		ss, err = r.Read()
++		if err != nil {
++			return err
++		}
++	}
++
++	out := make(map[string]string, len(ss))
++	for _, pair := range ss {
++		kv := strings.SplitN(pair, "=", 2)
++		if len(kv) != 2 {
++			return fmt.Errorf("%s must be formatted as key=value", pair)
++		}
++		out[kv[0]] = kv[1]
++	}
++	if !s.changed {
++		*s.value = out
++	} else {
++		for k, v := range out {
++			(*s.value)[k] = v
++		}
++	}
++	s.changed = true
++	return nil
++}
++
++func (s *stringToStringValue) Type() string {
++	return "stringToString"
++}
++
++func (s *stringToStringValue) String() string {
++	records := make([]string, 0, len(*s.value)>>1)
++	for k, v := range *s.value {
++		records = append(records, k+"="+v)
++	}
++
++	var buf bytes.Buffer
++	w := csv.NewWriter(&buf)
++	if err := w.Write(records); err != nil {
++		panic(err)
++	}
++	w.Flush()
++	return "[" + strings.TrimSpace(buf.String()) + "]"
++}
++
++func stringToStringConv(val string) (interface{}, error) {
++	val = strings.Trim(val, "[]")
++	// An empty string would cause an empty map
++	if len(val) == 0 {
++		return map[string]string{}, nil
++	}
++	r := csv.NewReader(strings.NewReader(val))
++	ss, err := r.Read()
++	if err != nil {
++		return nil, err
++	}
++	out := make(map[string]string, len(ss))
++	for _, pair := range ss {
++		kv := strings.SplitN(pair, "=", 2)
++		if len(kv) != 2 {
++			return nil, fmt.Errorf("%s must be formatted as key=value", pair)
++		}
++		out[kv[0]] = kv[1]
++	}
++	return out, nil
++}
++
++// GetStringToString return the map[string]string value of a flag with the given name
++func (f *FlagSet) GetStringToString(name string) (map[string]string, error) {
++	val, err := f.getFlagType(name, "stringToString", stringToStringConv)
++	if err != nil {
++		return map[string]string{}, err
++	}
++	return val.(map[string]string), nil
++}
++
++// StringToStringVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a map[string]string variable in which to store the values of the multiple flags.
++// The value of each argument will not try to be separated by comma
++func (f *FlagSet) StringToStringVar(p *map[string]string, name string, value map[string]string, usage string) {
++	f.VarP(newStringToStringValue(value, p), name, "", usage)
++}
++
++// StringToStringVarP is like StringToStringVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringToStringVarP(p *map[string]string, name, shorthand string, value map[string]string, usage string) {
++	f.VarP(newStringToStringValue(value, p), name, shorthand, usage)
++}
++
++// StringToStringVar defines a string flag with specified name, default value, and usage string.
++// The argument p points to a map[string]string variable in which to store the value of the flag.
++// The value of each argument will not try to be separated by comma
++func StringToStringVar(p *map[string]string, name string, value map[string]string, usage string) {
++	CommandLine.VarP(newStringToStringValue(value, p), name, "", usage)
++}
++
++// StringToStringVarP is like StringToStringVar, but accepts a shorthand letter that can be used after a single dash.
++func StringToStringVarP(p *map[string]string, name, shorthand string, value map[string]string, usage string) {
++	CommandLine.VarP(newStringToStringValue(value, p), name, shorthand, usage)
++}
++
++// StringToString defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a map[string]string variable that stores the value of the flag.
++// The value of each argument will not try to be separated by comma
++func (f *FlagSet) StringToString(name string, value map[string]string, usage string) *map[string]string {
++	p := map[string]string{}
++	f.StringToStringVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// StringToStringP is like StringToString, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) StringToStringP(name, shorthand string, value map[string]string, usage string) *map[string]string {
++	p := map[string]string{}
++	f.StringToStringVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// StringToString defines a string flag with specified name, default value, and usage string.
++// The return value is the address of a map[string]string variable that stores the value of the flag.
++// The value of each argument will not try to be separated by comma
++func StringToString(name string, value map[string]string, usage string) *map[string]string {
++	return CommandLine.StringToStringP(name, "", value, usage)
++}
++
++// StringToStringP is like StringToString, but accepts a shorthand letter that can be used after a single dash.
++func StringToStringP(name, shorthand string, value map[string]string, usage string) *map[string]string {
++	return CommandLine.StringToStringP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/uint.go b/thirdparty/pflag/uint.go
+new file mode 100644
+index 00000000..dcbc2b75
+--- /dev/null
++++ b/thirdparty/pflag/uint.go
+@@ -0,0 +1,88 @@
++package pflag
++
++import "strconv"
++
++// -- uint Value
++type uintValue uint
++
++func newUintValue(val uint, p *uint) *uintValue {
++	*p = val
++	return (*uintValue)(p)
++}
++
++func (i *uintValue) Set(s string) error {
++	v, err := strconv.ParseUint(s, 0, 64)
++	*i = uintValue(v)
++	return err
++}
++
++func (i *uintValue) Type() string {
++	return "uint"
++}
++
++func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i), 10) }
++
++func uintConv(sval string) (interface{}, error) {
++	v, err := strconv.ParseUint(sval, 0, 0)
++	if err != nil {
++		return 0, err
++	}
++	return uint(v), nil
++}
++
++// GetUint return the uint value of a flag with the given name
++func (f *FlagSet) GetUint(name string) (uint, error) {
++	val, err := f.getFlagType(name, "uint", uintConv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(uint), nil
++}
++
++// UintVar defines a uint flag with specified name, default value, and usage string.
++// The argument p points to a uint variable in which to store the value of the flag.
++func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) {
++	f.VarP(newUintValue(value, p), name, "", usage)
++}
++
++// UintVarP is like UintVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) UintVarP(p *uint, name, shorthand string, value uint, usage string) {
++	f.VarP(newUintValue(value, p), name, shorthand, usage)
++}
++
++// UintVar defines a uint flag with specified name, default value, and usage string.
++// The argument p points to a uint  variable in which to store the value of the flag.
++func UintVar(p *uint, name string, value uint, usage string) {
++	CommandLine.VarP(newUintValue(value, p), name, "", usage)
++}
++
++// UintVarP is like UintVar, but accepts a shorthand letter that can be used after a single dash.
++func UintVarP(p *uint, name, shorthand string, value uint, usage string) {
++	CommandLine.VarP(newUintValue(value, p), name, shorthand, usage)
++}
++
++// Uint defines a uint flag with specified name, default value, and usage string.
++// The return value is the address of a uint  variable that stores the value of the flag.
++func (f *FlagSet) Uint(name string, value uint, usage string) *uint {
++	p := new(uint)
++	f.UintVarP(p, name, "", value, usage)
++	return p
++}
++
++// UintP is like Uint, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) UintP(name, shorthand string, value uint, usage string) *uint {
++	p := new(uint)
++	f.UintVarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Uint defines a uint flag with specified name, default value, and usage string.
++// The return value is the address of a uint  variable that stores the value of the flag.
++func Uint(name string, value uint, usage string) *uint {
++	return CommandLine.UintP(name, "", value, usage)
++}
++
++// UintP is like Uint, but accepts a shorthand letter that can be used after a single dash.
++func UintP(name, shorthand string, value uint, usage string) *uint {
++	return CommandLine.UintP(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/uint16.go b/thirdparty/pflag/uint16.go
+new file mode 100644
+index 00000000..7e9914ed
+--- /dev/null
++++ b/thirdparty/pflag/uint16.go
+@@ -0,0 +1,88 @@
++package pflag
++
++import "strconv"
++
++// -- uint16 value
++type uint16Value uint16
++
++func newUint16Value(val uint16, p *uint16) *uint16Value {
++	*p = val
++	return (*uint16Value)(p)
++}
++
++func (i *uint16Value) Set(s string) error {
++	v, err := strconv.ParseUint(s, 0, 16)
++	*i = uint16Value(v)
++	return err
++}
++
++func (i *uint16Value) Type() string {
++	return "uint16"
++}
++
++func (i *uint16Value) String() string { return strconv.FormatUint(uint64(*i), 10) }
++
++func uint16Conv(sval string) (interface{}, error) {
++	v, err := strconv.ParseUint(sval, 0, 16)
++	if err != nil {
++		return 0, err
++	}
++	return uint16(v), nil
++}
++
++// GetUint16 return the uint16 value of a flag with the given name
++func (f *FlagSet) GetUint16(name string) (uint16, error) {
++	val, err := f.getFlagType(name, "uint16", uint16Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(uint16), nil
++}
++
++// Uint16Var defines a uint flag with specified name, default value, and usage string.
++// The argument p points to a uint variable in which to store the value of the flag.
++func (f *FlagSet) Uint16Var(p *uint16, name string, value uint16, usage string) {
++	f.VarP(newUint16Value(value, p), name, "", usage)
++}
++
++// Uint16VarP is like Uint16Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Uint16VarP(p *uint16, name, shorthand string, value uint16, usage string) {
++	f.VarP(newUint16Value(value, p), name, shorthand, usage)
++}
++
++// Uint16Var defines a uint flag with specified name, default value, and usage string.
++// The argument p points to a uint  variable in which to store the value of the flag.
++func Uint16Var(p *uint16, name string, value uint16, usage string) {
++	CommandLine.VarP(newUint16Value(value, p), name, "", usage)
++}
++
++// Uint16VarP is like Uint16Var, but accepts a shorthand letter that can be used after a single dash.
++func Uint16VarP(p *uint16, name, shorthand string, value uint16, usage string) {
++	CommandLine.VarP(newUint16Value(value, p), name, shorthand, usage)
++}
++
++// Uint16 defines a uint flag with specified name, default value, and usage string.
++// The return value is the address of a uint  variable that stores the value of the flag.
++func (f *FlagSet) Uint16(name string, value uint16, usage string) *uint16 {
++	p := new(uint16)
++	f.Uint16VarP(p, name, "", value, usage)
++	return p
++}
++
++// Uint16P is like Uint16, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Uint16P(name, shorthand string, value uint16, usage string) *uint16 {
++	p := new(uint16)
++	f.Uint16VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Uint16 defines a uint flag with specified name, default value, and usage string.
++// The return value is the address of a uint  variable that stores the value of the flag.
++func Uint16(name string, value uint16, usage string) *uint16 {
++	return CommandLine.Uint16P(name, "", value, usage)
++}
++
++// Uint16P is like Uint16, but accepts a shorthand letter that can be used after a single dash.
++func Uint16P(name, shorthand string, value uint16, usage string) *uint16 {
++	return CommandLine.Uint16P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/uint32.go b/thirdparty/pflag/uint32.go
+new file mode 100644
+index 00000000..d8024539
+--- /dev/null
++++ b/thirdparty/pflag/uint32.go
+@@ -0,0 +1,88 @@
++package pflag
++
++import "strconv"
++
++// -- uint32 value
++type uint32Value uint32
++
++func newUint32Value(val uint32, p *uint32) *uint32Value {
++	*p = val
++	return (*uint32Value)(p)
++}
++
++func (i *uint32Value) Set(s string) error {
++	v, err := strconv.ParseUint(s, 0, 32)
++	*i = uint32Value(v)
++	return err
++}
++
++func (i *uint32Value) Type() string {
++	return "uint32"
++}
++
++func (i *uint32Value) String() string { return strconv.FormatUint(uint64(*i), 10) }
++
++func uint32Conv(sval string) (interface{}, error) {
++	v, err := strconv.ParseUint(sval, 0, 32)
++	if err != nil {
++		return 0, err
++	}
++	return uint32(v), nil
++}
++
++// GetUint32 return the uint32 value of a flag with the given name
++func (f *FlagSet) GetUint32(name string) (uint32, error) {
++	val, err := f.getFlagType(name, "uint32", uint32Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(uint32), nil
++}
++
++// Uint32Var defines a uint32 flag with specified name, default value, and usage string.
++// The argument p points to a uint32 variable in which to store the value of the flag.
++func (f *FlagSet) Uint32Var(p *uint32, name string, value uint32, usage string) {
++	f.VarP(newUint32Value(value, p), name, "", usage)
++}
++
++// Uint32VarP is like Uint32Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Uint32VarP(p *uint32, name, shorthand string, value uint32, usage string) {
++	f.VarP(newUint32Value(value, p), name, shorthand, usage)
++}
++
++// Uint32Var defines a uint32 flag with specified name, default value, and usage string.
++// The argument p points to a uint32  variable in which to store the value of the flag.
++func Uint32Var(p *uint32, name string, value uint32, usage string) {
++	CommandLine.VarP(newUint32Value(value, p), name, "", usage)
++}
++
++// Uint32VarP is like Uint32Var, but accepts a shorthand letter that can be used after a single dash.
++func Uint32VarP(p *uint32, name, shorthand string, value uint32, usage string) {
++	CommandLine.VarP(newUint32Value(value, p), name, shorthand, usage)
++}
++
++// Uint32 defines a uint32 flag with specified name, default value, and usage string.
++// The return value is the address of a uint32  variable that stores the value of the flag.
++func (f *FlagSet) Uint32(name string, value uint32, usage string) *uint32 {
++	p := new(uint32)
++	f.Uint32VarP(p, name, "", value, usage)
++	return p
++}
++
++// Uint32P is like Uint32, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Uint32P(name, shorthand string, value uint32, usage string) *uint32 {
++	p := new(uint32)
++	f.Uint32VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Uint32 defines a uint32 flag with specified name, default value, and usage string.
++// The return value is the address of a uint32  variable that stores the value of the flag.
++func Uint32(name string, value uint32, usage string) *uint32 {
++	return CommandLine.Uint32P(name, "", value, usage)
++}
++
++// Uint32P is like Uint32, but accepts a shorthand letter that can be used after a single dash.
++func Uint32P(name, shorthand string, value uint32, usage string) *uint32 {
++	return CommandLine.Uint32P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/uint64.go b/thirdparty/pflag/uint64.go
+new file mode 100644
+index 00000000..f62240f2
+--- /dev/null
++++ b/thirdparty/pflag/uint64.go
+@@ -0,0 +1,88 @@
++package pflag
++
++import "strconv"
++
++// -- uint64 Value
++type uint64Value uint64
++
++func newUint64Value(val uint64, p *uint64) *uint64Value {
++	*p = val
++	return (*uint64Value)(p)
++}
++
++func (i *uint64Value) Set(s string) error {
++	v, err := strconv.ParseUint(s, 0, 64)
++	*i = uint64Value(v)
++	return err
++}
++
++func (i *uint64Value) Type() string {
++	return "uint64"
++}
++
++func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i), 10) }
++
++func uint64Conv(sval string) (interface{}, error) {
++	v, err := strconv.ParseUint(sval, 0, 64)
++	if err != nil {
++		return 0, err
++	}
++	return uint64(v), nil
++}
++
++// GetUint64 return the uint64 value of a flag with the given name
++func (f *FlagSet) GetUint64(name string) (uint64, error) {
++	val, err := f.getFlagType(name, "uint64", uint64Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(uint64), nil
++}
++
++// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
++// The argument p points to a uint64 variable in which to store the value of the flag.
++func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) {
++	f.VarP(newUint64Value(value, p), name, "", usage)
++}
++
++// Uint64VarP is like Uint64Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string) {
++	f.VarP(newUint64Value(value, p), name, shorthand, usage)
++}
++
++// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
++// The argument p points to a uint64 variable in which to store the value of the flag.
++func Uint64Var(p *uint64, name string, value uint64, usage string) {
++	CommandLine.VarP(newUint64Value(value, p), name, "", usage)
++}
++
++// Uint64VarP is like Uint64Var, but accepts a shorthand letter that can be used after a single dash.
++func Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string) {
++	CommandLine.VarP(newUint64Value(value, p), name, shorthand, usage)
++}
++
++// Uint64 defines a uint64 flag with specified name, default value, and usage string.
++// The return value is the address of a uint64 variable that stores the value of the flag.
++func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 {
++	p := new(uint64)
++	f.Uint64VarP(p, name, "", value, usage)
++	return p
++}
++
++// Uint64P is like Uint64, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Uint64P(name, shorthand string, value uint64, usage string) *uint64 {
++	p := new(uint64)
++	f.Uint64VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Uint64 defines a uint64 flag with specified name, default value, and usage string.
++// The return value is the address of a uint64 variable that stores the value of the flag.
++func Uint64(name string, value uint64, usage string) *uint64 {
++	return CommandLine.Uint64P(name, "", value, usage)
++}
++
++// Uint64P is like Uint64, but accepts a shorthand letter that can be used after a single dash.
++func Uint64P(name, shorthand string, value uint64, usage string) *uint64 {
++	return CommandLine.Uint64P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/uint8.go b/thirdparty/pflag/uint8.go
+new file mode 100644
+index 00000000..bb0e83c1
+--- /dev/null
++++ b/thirdparty/pflag/uint8.go
+@@ -0,0 +1,88 @@
++package pflag
++
++import "strconv"
++
++// -- uint8 Value
++type uint8Value uint8
++
++func newUint8Value(val uint8, p *uint8) *uint8Value {
++	*p = val
++	return (*uint8Value)(p)
++}
++
++func (i *uint8Value) Set(s string) error {
++	v, err := strconv.ParseUint(s, 0, 8)
++	*i = uint8Value(v)
++	return err
++}
++
++func (i *uint8Value) Type() string {
++	return "uint8"
++}
++
++func (i *uint8Value) String() string { return strconv.FormatUint(uint64(*i), 10) }
++
++func uint8Conv(sval string) (interface{}, error) {
++	v, err := strconv.ParseUint(sval, 0, 8)
++	if err != nil {
++		return 0, err
++	}
++	return uint8(v), nil
++}
++
++// GetUint8 return the uint8 value of a flag with the given name
++func (f *FlagSet) GetUint8(name string) (uint8, error) {
++	val, err := f.getFlagType(name, "uint8", uint8Conv)
++	if err != nil {
++		return 0, err
++	}
++	return val.(uint8), nil
++}
++
++// Uint8Var defines a uint8 flag with specified name, default value, and usage string.
++// The argument p points to a uint8 variable in which to store the value of the flag.
++func (f *FlagSet) Uint8Var(p *uint8, name string, value uint8, usage string) {
++	f.VarP(newUint8Value(value, p), name, "", usage)
++}
++
++// Uint8VarP is like Uint8Var, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Uint8VarP(p *uint8, name, shorthand string, value uint8, usage string) {
++	f.VarP(newUint8Value(value, p), name, shorthand, usage)
++}
++
++// Uint8Var defines a uint8 flag with specified name, default value, and usage string.
++// The argument p points to a uint8 variable in which to store the value of the flag.
++func Uint8Var(p *uint8, name string, value uint8, usage string) {
++	CommandLine.VarP(newUint8Value(value, p), name, "", usage)
++}
++
++// Uint8VarP is like Uint8Var, but accepts a shorthand letter that can be used after a single dash.
++func Uint8VarP(p *uint8, name, shorthand string, value uint8, usage string) {
++	CommandLine.VarP(newUint8Value(value, p), name, shorthand, usage)
++}
++
++// Uint8 defines a uint8 flag with specified name, default value, and usage string.
++// The return value is the address of a uint8 variable that stores the value of the flag.
++func (f *FlagSet) Uint8(name string, value uint8, usage string) *uint8 {
++	p := new(uint8)
++	f.Uint8VarP(p, name, "", value, usage)
++	return p
++}
++
++// Uint8P is like Uint8, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) Uint8P(name, shorthand string, value uint8, usage string) *uint8 {
++	p := new(uint8)
++	f.Uint8VarP(p, name, shorthand, value, usage)
++	return p
++}
++
++// Uint8 defines a uint8 flag with specified name, default value, and usage string.
++// The return value is the address of a uint8 variable that stores the value of the flag.
++func Uint8(name string, value uint8, usage string) *uint8 {
++	return CommandLine.Uint8P(name, "", value, usage)
++}
++
++// Uint8P is like Uint8, but accepts a shorthand letter that can be used after a single dash.
++func Uint8P(name, shorthand string, value uint8, usage string) *uint8 {
++	return CommandLine.Uint8P(name, shorthand, value, usage)
++}
+diff --git a/thirdparty/pflag/uint_slice.go b/thirdparty/pflag/uint_slice.go
+new file mode 100644
+index 00000000..edd94c60
+--- /dev/null
++++ b/thirdparty/pflag/uint_slice.go
+@@ -0,0 +1,126 @@
++package pflag
++
++import (
++	"fmt"
++	"strconv"
++	"strings"
++)
++
++// -- uintSlice Value
++type uintSliceValue struct {
++	value   *[]uint
++	changed bool
++}
++
++func newUintSliceValue(val []uint, p *[]uint) *uintSliceValue {
++	uisv := new(uintSliceValue)
++	uisv.value = p
++	*uisv.value = val
++	return uisv
++}
++
++func (s *uintSliceValue) Set(val string) error {
++	ss := strings.Split(val, ",")
++	out := make([]uint, len(ss))
++	for i, d := range ss {
++		u, err := strconv.ParseUint(d, 10, 0)
++		if err != nil {
++			return err
++		}
++		out[i] = uint(u)
++	}
++	if !s.changed {
++		*s.value = out
++	} else {
++		*s.value = append(*s.value, out...)
++	}
++	s.changed = true
++	return nil
++}
++
++func (s *uintSliceValue) Type() string {
++	return "uintSlice"
++}
++
++func (s *uintSliceValue) String() string {
++	out := make([]string, len(*s.value))
++	for i, d := range *s.value {
++		out[i] = fmt.Sprintf("%d", d)
++	}
++	return "[" + strings.Join(out, ",") + "]"
++}
++
++func uintSliceConv(val string) (interface{}, error) {
++	val = strings.Trim(val, "[]")
++	// Empty string would cause a slice with one (empty) entry
++	if len(val) == 0 {
++		return []uint{}, nil
++	}
++	ss := strings.Split(val, ",")
++	out := make([]uint, len(ss))
++	for i, d := range ss {
++		u, err := strconv.ParseUint(d, 10, 0)
++		if err != nil {
++			return nil, err
++		}
++		out[i] = uint(u)
++	}
++	return out, nil
++}
++
++// GetUintSlice returns the []uint value of a flag with the given name.
++func (f *FlagSet) GetUintSlice(name string) ([]uint, error) {
++	val, err := f.getFlagType(name, "uintSlice", uintSliceConv)
++	if err != nil {
++		return []uint{}, err
++	}
++	return val.([]uint), nil
++}
++
++// UintSliceVar defines a uintSlice flag with specified name, default value, and usage string.
++// The argument p points to a []uint variable in which to store the value of the flag.
++func (f *FlagSet) UintSliceVar(p *[]uint, name string, value []uint, usage string) {
++	f.VarP(newUintSliceValue(value, p), name, "", usage)
++}
++
++// UintSliceVarP is like UintSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) UintSliceVarP(p *[]uint, name, shorthand string, value []uint, usage string) {
++	f.VarP(newUintSliceValue(value, p), name, shorthand, usage)
++}
++
++// UintSliceVar defines a uint[] flag with specified name, default value, and usage string.
++// The argument p points to a uint[] variable in which to store the value of the flag.
++func UintSliceVar(p *[]uint, name string, value []uint, usage string) {
++	CommandLine.VarP(newUintSliceValue(value, p), name, "", usage)
++}
++
++// UintSliceVarP is like the UintSliceVar, but accepts a shorthand letter that can be used after a single dash.
++func UintSliceVarP(p *[]uint, name, shorthand string, value []uint, usage string) {
++	CommandLine.VarP(newUintSliceValue(value, p), name, shorthand, usage)
++}
++
++// UintSlice defines a []uint flag with specified name, default value, and usage string.
++// The return value is the address of a []uint variable that stores the value of the flag.
++func (f *FlagSet) UintSlice(name string, value []uint, usage string) *[]uint {
++	p := []uint{}
++	f.UintSliceVarP(&p, name, "", value, usage)
++	return &p
++}
++
++// UintSliceP is like UintSlice, but accepts a shorthand letter that can be used after a single dash.
++func (f *FlagSet) UintSliceP(name, shorthand string, value []uint, usage string) *[]uint {
++	p := []uint{}
++	f.UintSliceVarP(&p, name, shorthand, value, usage)
++	return &p
++}
++
++// UintSlice defines a []uint flag with specified name, default value, and usage string.
++// The return value is the address of a []uint variable that stores the value of the flag.
++func UintSlice(name string, value []uint, usage string) *[]uint {
++	return CommandLine.UintSliceP(name, "", value, usage)
++}
++
++// UintSliceP is like UintSlice, but accepts a shorthand letter that can be used after a single dash.
++func UintSliceP(name, shorthand string, value []uint, usage string) *[]uint {
++	return CommandLine.UintSliceP(name, shorthand, value, usage)
++}
+-- 
+2.19.2
+
diff --git a/package/docker-cli/docker-cli.mk b/package/docker-cli/docker-cli.mk
index c3dd536937..2624549298 100644
--- a/package/docker-cli/docker-cli.mk
+++ b/package/docker-cli/docker-cli.mk
@@ -6,7 +6,6 @@
 
 DOCKER_CLI_VERSION = v18.09.2
 DOCKER_CLI_SITE = $(call github,docker,cli,$(DOCKER_CLI_VERSION))
-DOCKER_CLI_WORKSPACE = gopath
 
 DOCKER_CLI_LICENSE = Apache-2.0
 DOCKER_CLI_LICENSE_FILES = LICENSE
@@ -14,7 +13,7 @@ DOCKER_CLI_LICENSE_FILES = LICENSE
 DOCKER_CLI_DEPENDENCIES = host-pkgconf
 
 DOCKER_CLI_TAGS = autogen
-DOCKER_CLI_BUILD_TARGETS = cmd/docker
+DOCKER_CLI_BUILD_TARGETS = github.com/docker/cli/cmd/docker
 
 DOCKER_CLI_LDFLAGS = \
 	-X github.com/docker/cli/cli.GitCommit=$(DOCKER_CLI_VERSION) \
diff --git a/package/docker-cli/go.mod b/package/docker-cli/go.mod
new file mode 100644
index 0000000000..0d585ca894
--- /dev/null
+++ b/package/docker-cli/go.mod
@@ -0,0 +1,127 @@
+module github.com/docker/cli
+
+// v18.09.2
+replace github.com/docker/docker => github.com/docker/engine v0.0.0-20190206233949-eb137ff1765f
+
+// In-tree pflag fixup
+replace github.com/spf13/pflag => ./thirdparty/pflag
+
+// Derived from vendor.conf with go mod init
+require (
+	cloud.google.com/go v0.37.0 // indirect
+	github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
+	github.com/Jeffail/gabs v1.2.0 // indirect
+	github.com/Microsoft/go-winio v0.4.10 // indirect
+	github.com/Microsoft/hcsshim v0.0.0-20180822151309-44c060121b68 // indirect
+	github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5
+	github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
+	github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
+	github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf // indirect
+	github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect
+	github.com/bitly/go-simplejson v0.5.0 // indirect
+	github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
+	github.com/boltdb/bolt v1.3.1 // indirect
+	github.com/bugsnag/bugsnag-go v1.4.0 // indirect
+	github.com/bugsnag/panicwrap v1.2.0 // indirect
+	github.com/cenkalti/backoff v2.1.1+incompatible // indirect
+	github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e // indirect
+	github.com/containerd/cgroups v0.0.0-20190226200435-dbea6f2bd416 // indirect
+	github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1
+	github.com/containerd/containerd v0.0.0-20180912143703-bb0f83ab6eec
+	github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8 // indirect
+	github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 // indirect
+	github.com/containerd/typeurl v0.0.0-20170912152501-f6943554a7e7 // indirect
+	github.com/coreos/go-systemd v0.0.0-20190212144455-93d5ec2c7f76 // indirect
+	github.com/cpuguy83/go-md2man v1.0.8
+	github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect
+	github.com/dgrijalva/jwt-go v2.6.0+incompatible // indirect
+	github.com/docker/distribution v0.0.0-20180327202408-83389a148052
+	github.com/docker/docker v0.0.0-00010101000000-000000000000
+	github.com/docker/docker-credential-helpers v0.6.1
+	github.com/docker/go v0.0.0-20160303222718-d30aec9fd63c // indirect
+	github.com/docker/go-connections v0.4.0
+	github.com/docker/go-events v0.0.0-20170721190031-9461782956ad // indirect
+	github.com/docker/go-metrics v0.0.0-20170502235133-d466d4f6fd96 // indirect
+	github.com/docker/go-units v0.3.3
+	github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4 // indirect
+	github.com/docker/licensing v0.0.0-20181018214158-1c117a1720cb
+	github.com/docker/swarmkit v0.0.0-20180820032850-cfa742c8abe6
+	github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
+	github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568
+	github.com/go-sql-driver/mysql v1.4.1 // indirect
+	github.com/godbus/dbus v4.1.0+incompatible // indirect
+	github.com/gofrs/uuid v3.2.0+incompatible // indirect
+	github.com/gogo/googleapis v0.0.0-20180501115203-b23578765ee5 // indirect
+	github.com/gogo/protobuf v1.1.1
+	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
+	github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
+	github.com/google/certificate-transparency-go v1.0.21 // indirect
+	github.com/google/go-cmp v0.2.0
+	github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
+	github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 // indirect
+	github.com/googleapis/gnostic v0.2.0 // indirect
+	github.com/gorilla/context v1.1.1 // indirect
+	github.com/gorilla/mux v1.6.2 // indirect
+	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
+	github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
+	github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
+	github.com/hashicorp/go-version v0.0.0-20180322230233-23480c066577
+	github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 // indirect
+	github.com/imdario/mergo v0.3.6
+	github.com/inconshreveable/mousetrap v1.0.0 // indirect
+	github.com/jinzhu/gorm v1.9.2 // indirect
+	github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect
+	github.com/jinzhu/now v1.0.0 // indirect
+	github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be // indirect
+	github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
+	github.com/lib/pq v1.0.0 // indirect
+	github.com/mattn/go-shellwords v1.0.3
+	github.com/mattn/go-sqlite3 v1.10.0 // indirect
+	github.com/miekg/pkcs11 v0.0.0-20180425180052-287d9350987c // indirect
+	github.com/mitchellh/mapstructure v1.1.2
+	github.com/moby/buildkit v0.0.0-20181005022200-520201006c9d
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
+	github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c
+	github.com/onsi/ginkgo v1.8.0 // indirect
+	github.com/onsi/gomega v1.5.0 // indirect
+	github.com/opencontainers/go-digest v1.0.0-rc1
+	github.com/opencontainers/image-spec v1.0.1
+	github.com/opencontainers/runc v0.0.0-20180815054256-20aff4f0488c // indirect
+	github.com/opencontainers/runtime-spec v1.0.1
+	github.com/opentracing/opentracing-go v0.0.0-20171003133519-1361b9cd60be // indirect
+	github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
+	github.com/pkg/errors v0.0.0-20161002052512-839d9e913e06
+	github.com/satori/go.uuid v0.0.0-20151028231719-d41af8bb6a77 // indirect
+	github.com/sirupsen/logrus v1.0.6
+	github.com/spf13/cobra v0.0.3
+	github.com/spf13/pflag v1.0.4-0.20181223182923-24fa6976df40
+	github.com/spf13/viper v1.3.2 // indirect
+	github.com/stretchr/testify v1.3.0 // indirect
+	github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b // indirect
+	github.com/theupdateframework/notary v0.6.1
+	github.com/tonistiigi/fsutil v0.0.0-20181002165410-f567071bed24
+	github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
+	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+	github.com/xeipuuv/gojsonschema v0.0.0-20160323030313-93e72a773fad
+	github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 // indirect
+	golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9
+	golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6
+	golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a
+	golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2
+	golang.org/x/time v0.0.0-20181108054448-85acf8d2951c
+	gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
+	gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect
+	gopkg.in/fatih/pool.v2 v2.0.0 // indirect
+	gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
+	gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect
+	gopkg.in/yaml.v2 v2.2.2
+	gotest.tools v2.1.0+incompatible
+	k8s.io/api v0.0.0-20180712090710-2d6f90ab1293
+	k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d
+	k8s.io/client-go v0.0.0-20180806134042-1f13a808da65
+	k8s.io/kube-openapi v0.0.0-20180719232738-d8ea2fe547a4 // indirect
+	k8s.io/kubernetes v1.11.2
+	vbom.ml/util v0.0.0-20170409195630-256737ac55c4
+)
diff --git a/package/docker-cli/go.sum b/package/docker-cli/go.sum
new file mode 100644
index 0000000000..5e80f04643
--- /dev/null
+++ b/package/docker-cli/go.sum
@@ -0,0 +1,419 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.0 h1:69FNAINiZfsEuwH3fKq8QrAAnHz+2m4XL4kVYi5BX0Q=
+cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Jeffail/gabs v1.2.0 h1:uFhoIVTtsX7hV2RxNgWad8gMU+8OJdzFbOathJdhD3o=
+github.com/Jeffail/gabs v1.2.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
+github.com/Microsoft/go-winio v0.4.10 h1:NrhPZI+cp3Fjmm5t/PZkVuir43JIRLZG/PSKK7atSfw=
+github.com/Microsoft/go-winio v0.4.10/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/hcsshim v0.0.0-20180822151309-44c060121b68 h1:Ql7fiqPfA1qtyv+herh2EwrWoPfbxBczLFTRMRywFEg=
+github.com/Microsoft/hcsshim v0.0.0-20180822151309-44c060121b68/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Nvveen/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:zxGnnE1K701Osh7fbn1bvr8DIts63ihxk3ppwvGkLyA=
+github.com/Nvveen/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
+github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
+github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
+github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
+github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
+github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
+github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
+github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
+github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
+github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/bugsnag/bugsnag-go v1.4.0 h1:CLCt5wO6/P0GelBEMRrlF52XveQMnnXHoCoxGZ+8a5g=
+github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABFQA=
+github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
+github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
+github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e h1:Qux+lbuMaRzkQyTdzgtz8MgzPtzmaPQy6DXmxpdxT3U=
+github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
+github.com/containerd/cgroups v0.0.0-20190226200435-dbea6f2bd416 h1:AaSMvkPaxfZD/OsDVBueAKzY5lnWAqLWgUivNg37WHA=
+github.com/containerd/cgroups v0.0.0-20190226200435-dbea6f2bd416/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1 h1:uict5mhHFTzKLUCufdSLym7z/J0CbBJT59lYbP9wtbg=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/containerd v0.0.0-20180912143703-bb0f83ab6eec h1:J3elAHUiHxWEccjiGlbF2LZKhcnAHL70NeX0PF7mK4s=
+github.com/containerd/containerd v0.0.0-20180912143703-bb0f83ab6eec/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8 h1:ZZOFPzvZO3N0f4LIQvZi68F2XDAMl/gqBfFMVjY6B3Y=
+github.com/containerd/continuity v0.0.0-20180216233310-d8fb8589b0e8/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 h1:XGyg7oTtD0DoRFhbpV6x1WfV0flKC4UxXU7ab1zC08U=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/typeurl v0.0.0-20170912152501-f6943554a7e7 h1:YLPsBBeWkiuDaXl6yV5sJI4PUo6JU3AKPfxtl/Xuukg=
+github.com/containerd/typeurl v0.0.0-20170912152501-f6943554a7e7/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
+github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190212144455-93d5ec2c7f76 h1:FE783w8WFh+Rvg+7bZ5g8p7gP4SeVS4AoNwkvazlsBg=
+github.com/coreos/go-systemd v0.0.0-20190212144455-93d5ec2c7f76/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/cpuguy83/go-md2man v1.0.8 h1:DwoNytLphI8hzS2Af4D0dfaEaiSq2bN05mEm4R6vf8M=
+github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 h1:tTngnoO/B6HQnJ+pK8tN7kEAhmhIfaJOutqq/A4/JTM=
+github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
+github.com/dgrijalva/jwt-go v2.6.0+incompatible h1:1O2NahKrpPnTN8Jy7ffR7S31wnTZIM4PEzrF8br1R4g=
+github.com/dgrijalva/jwt-go v2.6.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/docker/distribution v0.0.0-20180327202408-83389a148052 h1:bYklS+YB8BZreSEY+/WqaH+S8upfuYf0Hq/EmNOQMIA=
+github.com/docker/distribution v0.0.0-20180327202408-83389a148052/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker-credential-helpers v0.6.1 h1:Dq4iIfcM7cNtddhLVWe9h4QDjsi4OER3Z8voPu/I52g=
+github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
+github.com/docker/engine v0.0.0-20190206233949-eb137ff1765f h1:syROiDcQNWAcDNPfq3dkPdnMYeomRmIGR1sGsYKLGN8=
+github.com/docker/engine v0.0.0-20190206233949-eb137ff1765f/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY=
+github.com/docker/go v0.0.0-20160303222718-d30aec9fd63c h1:Ggg7IiOtghyZzn3ozi31kPHpV6qSjMgmesXaWCijYNM=
+github.com/docker/go v0.0.0-20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad h1:VXIse57M5C6ezDuCPyq6QmMvEJ2xclYKZ35SfkXdm3E=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-metrics v0.0.0-20170502235133-d466d4f6fd96 h1:HVQ/BC7Ze+bcVle903SvZMvncOcG2y3zI2K7i3jEHSM=
+github.com/docker/go-metrics v0.0.0-20170502235133-d466d4f6fd96/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
+github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
+github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+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=
+github.com/docker/licensing v0.0.0-20181018214158-1c117a1720cb h1:NbYsGDdef5DGBdT1YqtidriMgyJYc9nJi2rjqiGp+6w=
+github.com/docker/licensing v0.0.0-20181018214158-1c117a1720cb/go.mod h1:i6G9MMvTx3Dnr7TKD2OK5NYLQVoah2cRVKTnd1SbaGM=
+github.com/docker/swarmkit v0.0.0-20180820032850-cfa742c8abe6 h1:LPgH2mOqF8mBFTqMMR7fT7h6pVd4mC+loYyBoeYy+rM=
+github.com/docker/swarmkit v0.0.0-20180820032850-cfa742c8abe6/go.mod h1:n3Z4lIEl7g261ptkGDBcYi/3qBMDl9csaAhwi2MPejs=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
+github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
+github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI=
+github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
+github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
+github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/googleapis v0.0.0-20180501115203-b23578765ee5 h1:/IG8HCSoBr+VsVJYoJ+gf91i8ZX2JuLOqd4t9Wt9zN4=
+github.com/gogo/googleapis v0.0.0-20180501115203-b23578765ee5/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
+github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
+github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
+github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
+github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3UOWOvWQQKd+BoL3hcSCUWFLt0=
+github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
+github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
+github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
+github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
+github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
+github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
+github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
+github.com/hashicorp/go-version v0.0.0-20180322230233-23480c066577 h1:at4+18LrM8myamuV7/vT6x2s1JNXp2k4PsSbt4I02X4=
+github.com/hashicorp/go-version v0.0.0-20180322230233-23480c066577/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw=
+github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
+github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
+github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw=
+github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
+github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
+github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns=
+github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
+github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE=
+github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
+github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk=
+github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
+github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
+github.com/miekg/pkcs11 v0.0.0-20180425180052-287d9350987c h1:b4C1I45eTE1QpVdxxNcA4z/ZJphn+sSSzwrA/8ENUtI=
+github.com/miekg/pkcs11 v0.0.0-20180425180052-287d9350987c/go.mod h1:WCBAbTOdfhHhz7YXujeZMF7owC4tPb1naKFsgfUISjo=
+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.0.0-20181005022200-520201006c9d h1:cCqSeLRIV7rhE2NaJzOJgXDnH9U6n3T1PxfcZ5JsJcM=
+github.com/moby/buildkit v0.0.0-20181005022200-520201006c9d/go.mod h1:nnELdKPRkUAQR6pAB3mRU3+IlbqL3SSaAWqQL8k/K+4=
+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 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
+github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
+github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
+github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/runc v0.0.0-20180815054256-20aff4f0488c h1:xCoeOUJ3kIT4+LU3Fkcg7Kt62cr1xq4fGLVuDvBDSYc=
+github.com/opencontainers/runc v0.0.0-20180815054256-20aff4f0488c/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runtime-spec v1.0.1 h1:wY4pOY8fBdSIvs9+IDHC55thBuEulhzfSgKeC1yFvzQ=
+github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opentracing/opentracing-go v0.0.0-20171003133519-1361b9cd60be h1:vn0ruyYif1hUWDS2aEUdh6JGUfgK8gOOLpz/iTjb6pQ=
+github.com/opentracing/opentracing-go v0.0.0-20171003133519-1361b9cd60be/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
+github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pkg/errors v0.0.0-20161002052512-839d9e913e06 h1:gKQKvMjnwDvwtZxyd2HDEDNNMRkjbPN1V89eOLqovSU=
+github.com/pkg/errors v0.0.0-20161002052512-839d9e913e06/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e h1:n/3MEhJQjQxrOUCzh1Y3Re6aJUUWRp2M9+Oc3eVn/54=
+github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273 h1:agujYaXJSxSo18YNX3jzl+4G6Bstwt+kqv47GS12uL0=
+github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/satori/go.uuid v0.0.0-20151028231719-d41af8bb6a77 h1:X4eLA68fCmv+kFpUBFXIhv5wD5IFVB+oWUxjmpyl02c=
+github.com/satori/go.uuid v0.0.0-20151028231719-d41af8bb6a77/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
+github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
+github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
+github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b h1:UzwAjzrPQVJoxLfb26YI2WRrhD3g09ZHt9vAQckWiPY=
+github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/theupdateframework/notary v0.6.1 h1:7wshjstgS9x9F5LuB1L5mBI2xNMObWqjz+cjWoom6l0=
+github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY=
+github.com/tonistiigi/fsutil v0.0.0-20181002165410-f567071bed24 h1:W6Trnpx9I6lPEHVFiLq3l1CvzGFxL6+dknaJ9OU0jh0=
+github.com/tonistiigi/fsutil v0.0.0-20181002165410-f567071bed24/go.mod h1:eden9dLzAAuNQ0L7whFr6/Mzgz6btsvQpUnxOOI+CCE=
+github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
+github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+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=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v0.0.0-20160323030313-93e72a773fad h1:LIwN+8bLzKvIuCiV5yT1nICcW/8yNfU5jVV1SHhcPco=
+github.com/xeipuuv/gojsonschema v0.0.0-20160323030313-93e72a773fad/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
+github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 h1:j2hhcujLRHAg872RWAV5yaUrEjHEObwDv3aImCaNLek=
+github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440 h1:VOR2wHHZJgoALLvnlCN4JUaWACO1lOLXiSN2F3g/GXU=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
+gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/dancannon/gorethink.v3 v3.0.5 h1:/g7PWP7zUS6vSNmHSDbjCHQh1Rqn8Jy6zSMQxAsBSMQ=
+gopkg.in/dancannon/gorethink.v3 v3.0.5/go.mod h1:GXsi1e3N2OcKhcP6nsYABTiUejbWMFO4GY5a4pEaeEc=
+gopkg.in/fatih/pool.v2 v2.0.0 h1:xIFeWtxifuQJGk/IEPKsTduEKcKvPmhoiVDGpC40nKg=
+gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
+gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
+gopkg.in/gorethink/gorethink.v3 v3.0.5 h1:e2Uc/Xe+hpcVQFsj6MuHlYog3r0JYpnTzwDj/y2O4MU=
+gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
+gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+k8s.io/api v0.0.0-20180712090710-2d6f90ab1293 h1:hROmpFC7JMobXFXMmD7ZKZLhDKvr1IKfFJoYS/45G/8=
+k8s.io/api v0.0.0-20180712090710-2d6f90ab1293/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
+k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d h1:MZjlsu9igBoVPZkXpIGoxI6EonqNsXXZU7hhvfQLkd4=
+k8s.io/apimachinery v0.0.0-20180621070125-103fd098999d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
+k8s.io/client-go v0.0.0-20180806134042-1f13a808da65 h1:kQX7jEIMYrWV9XqFN4usRaBLzCu7fd/qsCXxbgf3+9g=
+k8s.io/client-go v0.0.0-20180806134042-1f13a808da65/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
+k8s.io/kube-openapi v0.0.0-20180719232738-d8ea2fe547a4 h1:C8xi0mJeE8wOFsLofmG7JVxRV2ZAgjYftRc9m2ypdmo=
+k8s.io/kube-openapi v0.0.0-20180719232738-d8ea2fe547a4/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
+k8s.io/kubernetes v1.11.2 h1:0XeIy/mHhEFiXkcCNn25muHIMJ/aYT2E9Uea40aD9Ck=
+k8s.io/kubernetes v1.11.2/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
+vbom.ml/util v0.0.0-20170409195630-256737ac55c4 h1:VRvrHgbw/Yg8mlNgD20TTQsTXTNJenU4jFUEqVxDRCU=
+vbom.ml/util v0.0.0-20170409195630-256737ac55c4/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
-- 
2.19.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 5/6] docker-proxy: upgrade to go modules
  2019-03-17  1:21 [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Christian Stewart
                   ` (2 preceding siblings ...)
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 4/6] docker-cli: " Christian Stewart
@ 2019-03-17  1:21 ` Christian Stewart
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 6/6] package/docker-engine: " Christian Stewart
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 20+ messages in thread
From: Christian Stewart @ 2019-03-17  1:21 UTC (permalink / raw)
  To: buildroot

This commit introduces go.mod and go.sum files derived from the vendor.conf in
the upstream docker-proxy repository.

Signed-off-by: Christian Stewart <christian@paral.in>
---
 package/docker-proxy/docker-proxy.mk |  4 +-
 package/docker-proxy/go.mod          | 46 ++++++++++++++++++++
 package/docker-proxy/go.sum          | 65 ++++++++++++++++++++++++++++
 3 files changed, 112 insertions(+), 3 deletions(-)
 create mode 100644 package/docker-proxy/go.mod
 create mode 100644 package/docker-proxy/go.sum

diff --git a/package/docker-proxy/docker-proxy.mk b/package/docker-proxy/docker-proxy.mk
index dfa9d4347d..4ea696d256 100644
--- a/package/docker-proxy/docker-proxy.mk
+++ b/package/docker-proxy/docker-proxy.mk
@@ -12,9 +12,7 @@ DOCKER_PROXY_LICENSE_FILES = LICENSE
 
 DOCKER_PROXY_DEPENDENCIES = host-pkgconf
 
-DOCKER_PROXY_WORKSPACE = gopath
-
-DOCKER_PROXY_BUILD_TARGETS = cmd/proxy
+DOCKER_PROXY_BUILD_TARGETS = github.com/docker/libnetwork/cmd/proxy
 
 define DOCKER_PROXY_INSTALL_TARGET_CMDS
 	$(INSTALL) -D -m 0755 $(@D)/bin/proxy $(TARGET_DIR)/usr/bin/docker-proxy
diff --git a/package/docker-proxy/go.mod b/package/docker-proxy/go.mod
new file mode 100644
index 0000000000..0a6daf4fd8
--- /dev/null
+++ b/package/docker-proxy/go.mod
@@ -0,0 +1,46 @@
+module github.com/docker/libnetwork
+
+// Derived from vendor.conf @ libnetwork
+// 7b2b1feb1de4817d522cc372af149ff48d25028e
+require (
+	github.com/Azure/go-ansiterm v0.0.0-20160425224146-04b7f292a41f
+	github.com/BurntSushi/toml v0.0.0-20150127021243-f706d00e3de6
+	github.com/Microsoft/go-winio v0.0.0-20160621211438-ce2922f643c8
+	github.com/Microsoft/hcsshim v0.5.7
+	github.com/Sirupsen/logrus v0.10.0
+	github.com/armon/go-metrics v0.0.0-20150106224455-eb0af217e5e9
+	github.com/armon/go-radix v0.0.0-20150105235045-e39d623f12e8
+	github.com/boltdb/bolt v1.2.0
+	github.com/codegangsta/cli v1.9.0
+	github.com/coreos/etcd v0.0.0-20170111022503-925d1d74cec8
+	github.com/coreos/go-systemd v0.0.0-20151104194251-b4a58d95188d
+	github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9
+	github.com/docker/docker v0.0.0-20170110083519-9c96768eae4b
+	github.com/docker/go-connections v0.2.0
+	github.com/docker/go-events v0.0.0-20160323060050-2e7d35281612
+	github.com/docker/go-units v0.0.0-20151222191443-8e2d4523730c
+	github.com/docker/libkv v0.0.0-20161109010621-1d8431073ae0
+	github.com/godbus/dbus v4.0.0+incompatible
+	github.com/gogo/protobuf v0.0.0-20160728085359-e33835a643a9
+	github.com/golang/protobuf v0.0.0-20150316215550-f7137ae6b19a
+	github.com/gorilla/context v0.0.0-20141217160251-215affda49ad
+	github.com/gorilla/mux v0.0.0-20150311031735-8096f4750345
+	github.com/hashicorp/consul v0.0.0-20150202214222-954aec66231b
+	github.com/hashicorp/go-msgpack v0.0.0-20140221154404-71c2886f5a67
+	github.com/hashicorp/go-multierror v0.0.0-20150107203116-2167c8ec4077
+	github.com/hashicorp/memberlist v0.0.0-20160329060508-88ac4de0d1a0
+	github.com/hashicorp/serf v0.0.0-20160317193612-598c54895cc5
+	github.com/mattn/go-shellwords v0.0.0-20160315040826-525bedee691b
+	github.com/miekg/dns v0.0.0-20151102115150-d27455715200
+	github.com/opencontainers/runc v0.0.0-20151215222150-ba1568de3993
+	github.com/pkg/errors v0.0.0-20161002052512-839d9e913e06
+	github.com/samuel/go-zookeeper v0.0.0-20150415181332-d0e0d8e11f31
+	github.com/seccomp/libseccomp-golang v0.0.0-20150813023252-1b506fc7c24e
+	github.com/stretchr/testify v0.0.0-20150512124233-dab07ac62d49
+	github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b
+	github.com/ugorji/go v0.0.0-20151028022000-f1f1a805ed36
+	github.com/vishvananda/netlink v0.0.0-20161115190107-17ea11b5a11c
+	github.com/vishvananda/netns v0.0.0-20150710222425-604eaf189ee8
+	golang.org/x/net v0.0.0-20150130075918-9dd48c277bcb
+	golang.org/x/sys v0.0.0-20160916181909-8f0908ab3b24
+)
diff --git a/package/docker-proxy/go.sum b/package/docker-proxy/go.sum
new file mode 100644
index 0000000000..4b5f1f477e
--- /dev/null
+++ b/package/docker-proxy/go.sum
@@ -0,0 +1,65 @@
+github.com/Azure/go-ansiterm v0.0.0-20160425224146-04b7f292a41f/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/BurntSushi/toml v0.0.0-20150127021243-f706d00e3de6 h1:16qw7tXeJQY74iT32JxxE3XfdkubpqAZ7GZrjkGOa+w=
+github.com/BurntSushi/toml v0.0.0-20150127021243-f706d00e3de6/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Microsoft/go-winio v0.0.0-20160621211438-ce2922f643c8/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/hcsshim v0.5.7/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Sirupsen/logrus v0.10.0 h1:I5b9VTLOttchcwWCzzNfRDAW2EFGlEN49hyoyq6d2ZI=
+github.com/Sirupsen/logrus v0.10.0/go.mod h1:rmk17hk6i8ZSAJkSDa7nOxamrG+SP4P0mm+DAvExv4U=
+github.com/armon/go-metrics v0.0.0-20150106224455-eb0af217e5e9 h1:j0r1R47jEcPk5M3GY3tFbv7q5J6j0Ppler3q4Guh6C0=
+github.com/armon/go-metrics v0.0.0-20150106224455-eb0af217e5e9/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20150105235045-e39d623f12e8 h1:XGHqlQXxwVly7mpcroyCGuEaGv/yvtS6r4PSHryDgxU=
+github.com/armon/go-radix v0.0.0-20150105235045-e39d623f12e8/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/boltdb/bolt v1.2.0 h1:4FPRe2N+UvvrhrU7U+A60z9qaK75Q0wDmxjxZrMntTQ=
+github.com/boltdb/bolt v1.2.0/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
+github.com/codegangsta/cli v1.9.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
+github.com/coreos/etcd v0.0.0-20170111022503-925d1d74cec8 h1:Ypfwv33VFaGSFpDIAqWldBZiuots/lZVIZWDWPiiCp4=
+github.com/coreos/etcd v0.0.0-20170111022503-925d1d74cec8/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-systemd v0.0.0-20151104194251-b4a58d95188d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9 h1:YpTz1+8tEHbybtxtMJNkV3U3GBAA05EakMRTR3dXkis=
+github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
+github.com/docker/docker v0.0.0-20170110083519-9c96768eae4b h1:bkHVtaH/cM9Z4RUjcdOxbsrn/Hl+xgxyOMMG0U58JCk=
+github.com/docker/docker v0.0.0-20170110083519-9c96768eae4b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/go-connections v0.2.0 h1:tV+S3i76CmPRYmR3NMDUFyr2HTP+3gL+xEPy146TPig=
+github.com/docker/go-connections v0.2.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20160323060050-2e7d35281612 h1:pPhPu07E7v8wBeuWMP/02w3xbcSzNJ1Rzj/oVK4lGY0=
+github.com/docker/go-events v0.0.0-20160323060050-2e7d35281612/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-units v0.0.0-20151222191443-8e2d4523730c/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libkv v0.0.0-20161109010621-1d8431073ae0 h1:lUF9wR5fJyfFCGoah5AK+JOS5lQVlMRXuOrma2kSYeg=
+github.com/docker/libkv v0.0.0-20161109010621-1d8431073ae0/go.mod h1:r5hEwHwW8dr0TFBYGCarMNbrQOiwL1xoqDYZ/JqoTK0=
+github.com/godbus/dbus v4.0.0+incompatible h1:iNJ3QcnEtQA2va/vj1d2Ng5Ld6tWno5HscHoVw9Bk/I=
+github.com/godbus/dbus v4.0.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/gogo/protobuf v0.0.0-20160728085359-e33835a643a9 h1:w/kXzxhPc2a5ghhMnq4XqZSazyxDgkwLsl/a3BCZqKo=
+github.com/gogo/protobuf v0.0.0-20160728085359-e33835a643a9/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/protobuf v0.0.0-20150316215550-f7137ae6b19a/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/gorilla/context v0.0.0-20141217160251-215affda49ad/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v0.0.0-20150311031735-8096f4750345/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/hashicorp/consul v0.0.0-20150202214222-954aec66231b h1:DiJuKbXl9IhD/2NNuBjUYYfrZnWaN6/HQvgRI6Bg1Pg=
+github.com/hashicorp/consul v0.0.0-20150202214222-954aec66231b/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
+github.com/hashicorp/go-msgpack v0.0.0-20140221154404-71c2886f5a67 h1:uUGuA3Cnfp7qbFpIMOCDVz3TaWIF4lLYM8PE3YHpoA4=
+github.com/hashicorp/go-msgpack v0.0.0-20140221154404-71c2886f5a67/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v0.0.0-20150107203116-2167c8ec4077 h1:zqkBGGSW/YAT6PWeY+/NvtHq/kCXgKDw90/X33EvbgE=
+github.com/hashicorp/go-multierror v0.0.0-20150107203116-2167c8ec4077/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+github.com/hashicorp/memberlist v0.0.0-20160329060508-88ac4de0d1a0 h1:CBRChEdr0X1Kg9nkAhu7UcNwwHt8VWuVsTppokn2C5A=
+github.com/hashicorp/memberlist v0.0.0-20160329060508-88ac4de0d1a0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE=
+github.com/hashicorp/serf v0.0.0-20160317193612-598c54895cc5 h1:wjD77m26jcVP6sb2+EQ0YNxSCqmlMs3vibnDs4kFWok=
+github.com/hashicorp/serf v0.0.0-20160317193612-598c54895cc5/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
+github.com/mattn/go-shellwords v0.0.0-20160315040826-525bedee691b/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/miekg/dns v0.0.0-20151102115150-d27455715200 h1:HjguPRc9EiUS7LUG1Flo+h1f3AXVVWLAsM2hwTVatBU=
+github.com/miekg/dns v0.0.0-20151102115150-d27455715200/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/opencontainers/runc v0.0.0-20151215222150-ba1568de3993 h1:ItpHk8RGctsgclMNoM76RRpTTSY6DAonhAqmVtSWcR4=
+github.com/opencontainers/runc v0.0.0-20151215222150-ba1568de3993/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/pkg/errors v0.0.0-20161002052512-839d9e913e06/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/samuel/go-zookeeper v0.0.0-20150415181332-d0e0d8e11f31 h1:K6A6FPUqjrujk315iZJFr8CLsW7zGhhmZDgGbghmleo=
+github.com/samuel/go-zookeeper v0.0.0-20150415181332-d0e0d8e11f31/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/seccomp/libseccomp-golang v0.0.0-20150813023252-1b506fc7c24e/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
+github.com/stretchr/testify v0.0.0-20150512124233-dab07ac62d49/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/ugorji/go v0.0.0-20151028022000-f1f1a805ed36 h1:vKlfv8sKDcjM5WIkcAzl5CZkKB8pppsrdmqczMTuapo=
+github.com/ugorji/go v0.0.0-20151028022000-f1f1a805ed36/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
+github.com/vishvananda/netlink v0.0.0-20161115190107-17ea11b5a11c h1:/7Zu77XQTwdsSR/vIABW0IoOwKCXMyqZxQkFJ91pXTQ=
+github.com/vishvananda/netlink v0.0.0-20161115190107-17ea11b5a11c/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
+github.com/vishvananda/netns v0.0.0-20150710222425-604eaf189ee8 h1:MmJ82dMUwQ+0jmeFg/iWripmd3D+GlYUu/CV2m7hJto=
+github.com/vishvananda/netns v0.0.0-20150710222425-604eaf189ee8/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
+golang.org/x/net v0.0.0-20150130075918-9dd48c277bcb h1:xefoN9sIYVG4bUez6GKQU4swTuYPYaaTYR8uju6oqNs=
+golang.org/x/net v0.0.0-20150130075918-9dd48c277bcb/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/sys v0.0.0-20160916181909-8f0908ab3b24/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-- 
2.19.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 6/6] package/docker-engine: upgrade to go modules
  2019-03-17  1:21 [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Christian Stewart
                   ` (3 preceding siblings ...)
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 5/6] docker-proxy: " Christian Stewart
@ 2019-03-17  1:21 ` Christian Stewart
  2019-03-27 16:50 ` [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Thomas Petazzoni
  2019-04-07 20:17 ` Thomas Petazzoni
  6 siblings, 0 replies; 20+ messages in thread
From: Christian Stewart @ 2019-03-17  1:21 UTC (permalink / raw)
  To: buildroot

This commit introduces in-tree go.mod and go.sum files derived from the
vendor.conf file from the upstream Docker engine repository.

The Docker vendor/ directory is overwritten by the Go module tool.

Signed-off-by: Christian Stewart <christian@paral.in>
---
 package/docker-engine/docker-engine.mk |   3 +-
 package/docker-engine/go.mod           | 124 ++++++++++++
 package/docker-engine/go.sum           | 256 +++++++++++++++++++++++++
 3 files changed, 381 insertions(+), 2 deletions(-)
 create mode 100644 package/docker-engine/go.mod
 create mode 100644 package/docker-engine/go.sum

diff --git a/package/docker-engine/docker-engine.mk b/package/docker-engine/docker-engine.mk
index e2f59666df..e433e94659 100644
--- a/package/docker-engine/docker-engine.mk
+++ b/package/docker-engine/docker-engine.mk
@@ -11,14 +11,13 @@ DOCKER_ENGINE_LICENSE = Apache-2.0
 DOCKER_ENGINE_LICENSE_FILES = LICENSE
 
 DOCKER_ENGINE_DEPENDENCIES = host-pkgconf
-DOCKER_ENGINE_SRC_SUBDIR = github.com/docker/docker
 
 DOCKER_ENGINE_LDFLAGS = \
 	-X main.GitCommit=$(DOCKER_ENGINE_VERSION) \
 	-X main.Version=$(DOCKER_ENGINE_VERSION)
 
 DOCKER_ENGINE_TAGS = cgo exclude_graphdriver_zfs autogen
-DOCKER_ENGINE_BUILD_TARGETS = cmd/dockerd
+DOCKER_ENGINE_BUILD_TARGETS = github.com/docker/docker/cmd/dockerd
 
 ifeq ($(BR2_PACKAGE_LIBSECCOMP),y)
 DOCKER_ENGINE_TAGS += seccomp
diff --git a/package/docker-engine/go.mod b/package/docker-engine/go.mod
new file mode 100644
index 0000000000..afebc32db3
--- /dev/null
+++ b/package/docker-engine/go.mod
@@ -0,0 +1,124 @@
+module github.com/docker/docker
+
+// Generated with "go mod init" against v18.09.2
+require (
+	cloud.google.com/go v0.23.0
+	github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78
+	github.com/BurntSushi/toml v0.0.0-20170626110600-a368813c5e64
+	github.com/Graylog2/go-gelf v0.0.0-20171211094031-414364622654
+	github.com/Microsoft/go-winio v0.4.11
+	github.com/Microsoft/hcsshim v0.7.12
+	github.com/Microsoft/opengcs v0.3.8
+	github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5
+	github.com/RackSec/srslog v0.0.0-20161121191927-456df3a81436
+	github.com/armon/go-metrics v0.0.0-20150106224455-eb0af217e5e9 // indirect
+	github.com/armon/go-radix v0.0.0-20150105235045-e39d623f12e8 // indirect
+	github.com/aws/aws-sdk-go v1.12.66
+	github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a // indirect
+	github.com/bfirsh/funker-go v0.0.0-20161231111542-eaa0a2e06f30
+	github.com/bsphere/le_go v0.0.0-20170215134836-7a984a84b549
+	github.com/cloudflare/cfssl v0.0.0-20180323000720-5d63dbd981b5
+	github.com/containerd/cgroups v0.0.0-20180515175038-5e610833b720
+	github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1 // indirect
+	github.com/containerd/containerd v1.2.2
+	github.com/containerd/continuity v0.0.0-20181001140422-bd77b46c8352
+	github.com/containerd/cri v0.0.0-20181220000310-0d5cabd006cb // indirect
+	github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260
+	github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3 // indirect
+	github.com/containerd/ttrpc v0.0.0-20180920185216-2a805f718635 // indirect
+	github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd
+	github.com/coreos/etcd v3.2.1+incompatible // indirect
+	github.com/coreos/go-semver v0.2.0 // indirect
+	github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
+	github.com/coreos/pkg v0.0.0-20160620232715-fa29b1d70f0b // indirect
+	github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9 // indirect
+	github.com/docker/distribution v0.0.0-20180327202408-83389a148052
+	github.com/docker/go-connections v0.4.0
+	github.com/docker/go-events v0.0.0-20170721190031-9461782956ad // indirect
+	github.com/docker/go-metrics v0.0.0-20170502235133-d466d4f6fd96
+	github.com/docker/go-units v0.3.3
+	github.com/docker/libkv v0.0.0-20180912205406-458977154600
+	github.com/docker/libnetwork v0.0.0-20181207154035-2cfbf9b1f981
+	github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4
+	github.com/docker/swarmkit v0.0.0-20181031215329-6186e40fb04a
+	github.com/fernet/fernet-go v0.0.0-20151007213151-1b2437bc582b // indirect
+	github.com/fluent/fluent-logger-golang v1.3.0
+	github.com/fsnotify/fsnotify v1.4.7
+	github.com/go-check/check v0.0.0-20180628173108-788fd7840127
+	github.com/go-ini/ini v1.25.4 // indirect
+	github.com/godbus/dbus v4.0.0+incompatible // indirect
+	github.com/gogo/googleapis v1.0.0 // indirect
+	github.com/gogo/protobuf v1.0.0
+	github.com/golang/gddo v0.0.0-20180130204405-9b12a26f3fbd
+	github.com/google/certificate-transparency-go v1.0.20 // indirect
+	github.com/google/go-cmp v0.2.0
+	github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 // indirect
+	github.com/googleapis/gax-go v2.0.0+incompatible // indirect
+	github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f // indirect
+	github.com/gorilla/mux v0.0.0-20160317213430-0eeaf8392f5b
+	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
+	github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
+	github.com/hashicorp/consul v0.5.2 // indirect
+	github.com/hashicorp/go-immutable-radix v1.0.0
+	github.com/hashicorp/go-memdb v0.0.0-20161216180745-cb9a474f84cc
+	github.com/hashicorp/go-msgpack v0.0.0-20140221154404-71c2886f5a67 // indirect
+	github.com/hashicorp/go-multierror v0.0.0-20150127051936-fcdddc395df1 // indirect
+	github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect
+	github.com/hashicorp/memberlist v0.0.0-20171201184301-3d8438da9589 // indirect
+	github.com/hashicorp/serf v0.0.0-20160317193612-598c54895cc5 // indirect
+	github.com/imdario/mergo v0.3.6
+	github.com/inconshreveable/mousetrap v1.0.0 // indirect
+	github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed // indirect
+	github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 // indirect
+	github.com/kr/pretty v0.1.0 // indirect
+	github.com/kr/pty v1.1.1
+	github.com/mattn/go-shellwords v1.0.3
+	github.com/matttproud/golang_protobuf_extensions v1.0.0 // indirect
+	github.com/miekg/dns v1.0.7 // indirect
+	github.com/mistifyio/go-zfs v0.0.0-20160425201758-22c9b32c84eb
+	github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 // indirect
+	github.com/moby/buildkit v0.0.0-20181210113810-d9f75920678e
+	github.com/opencontainers/go-digest v1.0.0-rc1
+	github.com/opencontainers/image-spec v1.0.1
+	github.com/opencontainers/runc v0.0.0-20181203215513-96ec2177ae84
+	github.com/opencontainers/runtime-spec v0.0.0-20180911193056-5684b8af48c1
+	github.com/opencontainers/selinux v0.0.0-20180628160156-b6fa367ed7f5
+	github.com/opentracing-contrib/go-stdlib v0.0.0-20171029140428-b1a47cfbdd75 // indirect
+	github.com/opentracing/opentracing-go v0.0.0-20171003133519-1361b9cd60be // indirect
+	github.com/pborman/uuid v0.0.0-20160209185913-a97ce2ca70fa
+	github.com/philhofer/fwd v0.0.0-20160129035939-98c11a7a6ec8 // indirect
+	github.com/pivotal-golang/clock v0.0.0-20151018222946-3fd3c1944c59 // indirect
+	github.com/pkg/errors v0.8.0
+	github.com/prometheus/client_golang v0.0.0-20160802072246-52437c81da6b
+	github.com/prometheus/client_model v0.0.0-20150212101744-fa8ad6fec335 // indirect
+	github.com/prometheus/common v0.0.0-20160801171955-ebdfc6da4652 // indirect
+	github.com/prometheus/procfs v0.0.0-20160411190841-abf152e5f3e9 // indirect
+	github.com/samuel/go-zookeeper v0.0.0-20150415181332-d0e0d8e11f31 // indirect
+	github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
+	github.com/seccomp/libseccomp-golang v0.0.0-20160531183505-32f571b70023
+	github.com/sirupsen/logrus v1.0.6
+	github.com/spf13/cobra v0.0.3
+	github.com/spf13/pflag v1.0.1
+	github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b
+	github.com/tchap/go-patricia v2.2.6+incompatible
+	github.com/tinylib/msgp v0.0.0-20171013044219-3b556c645408 // indirect
+	github.com/tonistiigi/fsutil v0.0.0-20181011223333-2862f6bc5ac9
+	github.com/ugorji/go v0.0.0-20151028022000-f1f1a805ed36 // indirect
+	github.com/vbatts/tar-split v0.11.0
+	github.com/vdemeester/shakers v0.1.0
+	github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e
+	github.com/vishvananda/netns v0.0.0-20150710222425-604eaf189ee8 // indirect
+	go.etcd.io/bbolt v1.3.1-etcd.8
+	go.opencensus.io v0.11.0 // indirect
+	golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 // indirect
+	golang.org/x/net v0.0.0-20180724234803-3673e40ba225
+	golang.org/x/oauth2 v0.0.0-20180529203656-ec22f46f877b // indirect
+	golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
+	golang.org/x/sys v0.0.0-20180715085529-ac767d655b30
+	golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2
+	google.golang.org/api v0.0.0-20180530150455-de943baf05a0 // indirect
+	google.golang.org/appengine v1.4.0 // indirect
+	google.golang.org/genproto v0.0.0-20180523212516-694d95ba50e6
+	google.golang.org/grpc v1.12.0
+	gotest.tools v2.1.0+incompatible
+)
diff --git a/package/docker-engine/go.sum b/package/docker-engine/go.sum
new file mode 100644
index 0000000000..fc3671107d
--- /dev/null
+++ b/package/docker-engine/go.sum
@@ -0,0 +1,256 @@
+cloud.google.com/go v0.23.0 h1:w1svupRqvZnfjN9+KksMiggoIRQuMzWkVzpxcR96xDs=
+cloud.google.com/go v0.23.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
+github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+github.com/BurntSushi/toml v0.0.0-20170626110600-a368813c5e64 h1:BuYewlQyh/jroxY8qx41SrzD8Go17GkyCyAeVmprvQI=
+github.com/BurntSushi/toml v0.0.0-20170626110600-a368813c5e64/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Graylog2/go-gelf v0.0.0-20171211094031-414364622654 h1:CFZ9rfJy1PkQmnXcFkC3SzB3BGowHe6USIbhyzPmq/U=
+github.com/Graylog2/go-gelf v0.0.0-20171211094031-414364622654/go.mod h1:fBaQWrftOD5CrVCUfoYGHs4X4VViTuGOXA8WloCjTY0=
+github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
+github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/hcsshim v0.7.12 h1:VCjS2UYlYyMfRnCus+yhbJZBi9DeFSMBKrggG/PAeHk=
+github.com/Microsoft/hcsshim v0.7.12/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/opengcs v0.3.8 h1:oQjUN6iSXYuzNKqWatebzkguhY2c+RnYgKn/ZsYSNnk=
+github.com/Microsoft/opengcs v0.3.8/go.mod h1:UtvrUBa6myDYv42xL4V4TESoORc30AU3S4IPsSXUUws=
+github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
+github.com/Nvveen/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:zxGnnE1K701Osh7fbn1bvr8DIts63ihxk3ppwvGkLyA=
+github.com/Nvveen/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
+github.com/RackSec/srslog v0.0.0-20161121191927-456df3a81436 h1:5QomwknbFZSmW2Hj8FFmm9PLOWTjzUMh5vxnskidroY=
+github.com/RackSec/srslog v0.0.0-20161121191927-456df3a81436/go.mod h1:cDLGBht23g0XQdLjzn6xOGXDkLK182YfINAaZEQLCHQ=
+github.com/armon/go-metrics v0.0.0-20150106224455-eb0af217e5e9 h1:j0r1R47jEcPk5M3GY3tFbv7q5J6j0Ppler3q4Guh6C0=
+github.com/armon/go-metrics v0.0.0-20150106224455-eb0af217e5e9/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20150105235045-e39d623f12e8 h1:XGHqlQXxwVly7mpcroyCGuEaGv/yvtS6r4PSHryDgxU=
+github.com/armon/go-radix v0.0.0-20150105235045-e39d623f12e8/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aws/aws-sdk-go v1.12.66 h1:CEkNJTon+Uii4VHlK/SbTrgvfyM18FXEhBeV4A9EgOc=
+github.com/aws/aws-sdk-go v1.12.66/go.mod h1:ZRmQr0FajVIyZ4ZzBYKG5P3ZqPz9IHG41ZoMu1ADI3k=
+github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a h1:BtpsbiV638WQZwhA98cEZw2BsbnQJrbd0BI7tsy0W1c=
+github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/bfirsh/funker-go v0.0.0-20161231111542-eaa0a2e06f30 h1:aohfn8zcZQQCVVlloY+MJTn3ZP6WSqnm2z+u8KKuPfk=
+github.com/bfirsh/funker-go v0.0.0-20161231111542-eaa0a2e06f30/go.mod h1:H8VUrG5CegCjHp0buzyJUcaGqnlkgLgFuKNgZMImCng=
+github.com/bsphere/le_go v0.0.0-20170215134836-7a984a84b549 h1:QJJnIXZ34OUK5JfWlq1l3n0SfO9g1amiLFIcTECgpq0=
+github.com/bsphere/le_go v0.0.0-20170215134836-7a984a84b549/go.mod h1:313oBJKClgRD/+t59eUnrfG7/xHXZJd7v+SjCacDm4Q=
+github.com/cloudflare/cfssl v0.0.0-20180323000720-5d63dbd981b5 h1:PqZ3bA4yzwywivzk7PBQWngJp2/PAS0bWRZerKteicY=
+github.com/cloudflare/cfssl v0.0.0-20180323000720-5d63dbd981b5/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
+github.com/containerd/cgroups v0.0.0-20180515175038-5e610833b720 h1:BWEq1Q9P+v4Yb97IRvoSMNuHbd2ilBqV9h5Zew3ELco=
+github.com/containerd/cgroups v0.0.0-20180515175038-5e610833b720/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1 h1:uict5mhHFTzKLUCufdSLym7z/J0CbBJT59lYbP9wtbg=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/containerd v1.2.2 h1:N3tAHxrX+byqfAsENdDWLSMtFD4thUxK7kFElUl+8z8=
+github.com/containerd/containerd v1.2.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/continuity v0.0.0-20181001140422-bd77b46c8352 h1:CdBoaTKPl60tksFVWYc5QnLWwXBcU+XcLiXx8+NcZ9o=
+github.com/containerd/continuity v0.0.0-20181001140422-bd77b46c8352/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/containerd/cri v0.0.0-20181220000310-0d5cabd006cb h1:/3uWDTGvs/oSv2NFb2rwSIB44MQbxffklvaCaihQFlU=
+github.com/containerd/cri v0.0.0-20181220000310-0d5cabd006cb/go.mod h1:DavH5Qa8+6jOmeOMO3dhWoqksucZDe06LfuhBz/xPZs=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 h1:XGyg7oTtD0DoRFhbpV6x1WfV0flKC4UxXU7ab1zC08U=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3 h1:esQOJREg8nw8aXj6uCN5dfW5cKUBiEJ/+nni1Q/D/sw=
+github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/ttrpc v0.0.0-20180920185216-2a805f718635 h1:Hh9KYLzbpTyhtCnW4p0Iy+bJNO4fGKFZp1ylELZw6TI=
+github.com/containerd/ttrpc v0.0.0-20180920185216-2a805f718635/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
+github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd h1:JNn81o/xG+8NEo3bC/vx9pbi/g2WI8mtP2/nXzu297Y=
+github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
+github.com/coreos/etcd v3.2.1+incompatible h1:jL1a22zvYFnbRhkezZsSv0tWhplb9kqhZCugdRtQrdE=
+github.com/coreos/etcd v3.2.1+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160620232715-fa29b1d70f0b h1:IqgHacj6F3QnV+0H9PXFWAmML5HdxkZakBQgZgfD+FU=
+github.com/coreos/pkg v0.0.0-20160620232715-fa29b1d70f0b/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9 h1:YpTz1+8tEHbybtxtMJNkV3U3GBAA05EakMRTR3dXkis=
+github.com/deckarep/golang-set v0.0.0-20141123011944-ef32fa3046d9/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
+github.com/docker/distribution v0.0.0-20180327202408-83389a148052 h1:bYklS+YB8BZreSEY+/WqaH+S8upfuYf0Hq/EmNOQMIA=
+github.com/docker/distribution v0.0.0-20180327202408-83389a148052/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
+github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad h1:VXIse57M5C6ezDuCPyq6QmMvEJ2xclYKZ35SfkXdm3E=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-metrics v0.0.0-20170502235133-d466d4f6fd96 h1:HVQ/BC7Ze+bcVle903SvZMvncOcG2y3zI2K7i3jEHSM=
+github.com/docker/go-metrics v0.0.0-20170502235133-d466d4f6fd96/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
+github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
+github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libkv v0.0.0-20180912205406-458977154600 h1:xxUnNhXC+fMHkbRhJ0RvqaKTiLLf8lppoDjFnhp8TKk=
+github.com/docker/libkv v0.0.0-20180912205406-458977154600/go.mod h1:r5hEwHwW8dr0TFBYGCarMNbrQOiwL1xoqDYZ/JqoTK0=
+github.com/docker/libnetwork v0.0.0-20181207154035-2cfbf9b1f981 h1:ZV+ZpVI7RRqsqFcXsZg9UO7zevb5JDZTcjFxeIecy5M=
+github.com/docker/libnetwork v0.0.0-20181207154035-2cfbf9b1f981/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=
+github.com/docker/swarmkit v0.0.0-20181031215329-6186e40fb04a h1:Ap+Jb5hib7hVoGb7mp4PornwzeDF4mC7mJ8eELiFYMU=
+github.com/docker/swarmkit v0.0.0-20181031215329-6186e40fb04a/go.mod h1:n3Z4lIEl7g261ptkGDBcYi/3qBMDl9csaAhwi2MPejs=
+github.com/fernet/fernet-go v0.0.0-20151007213151-1b2437bc582b h1:QqmfGmPkAbYcqM0YdHOS8JxqRJqEx+0rxjYZ1OiP6aw=
+github.com/fernet/fernet-go v0.0.0-20151007213151-1b2437bc582b/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
+github.com/fluent/fluent-logger-golang v1.3.0 h1:oBolFKS9fY9HReChzaX1RQF5GkdNdByrledPTfUWoGA=
+github.com/fluent/fluent-logger-golang v1.3.0/go.mod h1:2/HCT/jTy78yGyeNGQLGQsjF3zzzAuy6Xlk6FCMV5eU=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI=
+github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
+github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
+github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
+github.com/godbus/dbus v4.0.0+incompatible h1:iNJ3QcnEtQA2va/vj1d2Ng5Ld6tWno5HscHoVw9Bk/I=
+github.com/godbus/dbus v4.0.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/gogo/googleapis v1.0.0 h1:7wwW6yQ5xmZE42/QWNC87xHgnHxIh7pWvtc1BhI/0DU=
+github.com/gogo/googleapis v1.0.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
+github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM=
+github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/gddo v0.0.0-20180130204405-9b12a26f3fbd h1:zMFRhZeBUbsih4Bn2CntQIKSzThPiHhlGifnbsJm03o=
+github.com/golang/gddo v0.0.0-20180130204405-9b12a26f3fbd/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
+github.com/golang/protobuf v1.1.0 h1:0iH4Ffd/meGoXqF2lSAhZHt8X+cPgkfn/cb6Cce5Vpc=
+github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/certificate-transparency-go v1.0.20 h1:azETE79toaBOyp+StoEBy8atzQujL0PyBPEmsEeDCXI=
+github.com/google/certificate-transparency-go v1.0.20/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
+github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3UOWOvWQQKd+BoL3hcSCUWFLt0=
+github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
+github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f h1:9oNbS1z4rVpbnkHBdPZU4jo9bSmrLpII768arSyMFgk=
+github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v0.0.0-20160317213430-0eeaf8392f5b h1:4OcRAUBSclcjVrZ8n7/pl9iWUx7JXw4Mj6bww0ZlBhE=
+github.com/gorilla/mux v0.0.0-20160317213430-0eeaf8392f5b/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
+github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
+github.com/hashicorp/consul v0.5.2 h1:Y/bFTk0dk3FLXDvQD4BI0SygNEduwCHOk1llE8W3ArY=
+github.com/hashicorp/consul v0.5.2/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
+github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-memdb v0.0.0-20161216180745-cb9a474f84cc h1:+a6OGop8lqksGF5BgpRVghkeR3vy2HDa7lDKx6UvSRE=
+github.com/hashicorp/go-memdb v0.0.0-20161216180745-cb9a474f84cc/go.mod h1:kbfItVoBJwCfKXDXN4YoAXjxcFVZ7MRrJzyTX6H4giE=
+github.com/hashicorp/go-msgpack v0.0.0-20140221154404-71c2886f5a67 h1:uUGuA3Cnfp7qbFpIMOCDVz3TaWIF4lLYM8PE3YHpoA4=
+github.com/hashicorp/go-msgpack v0.0.0-20140221154404-71c2886f5a67/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v0.0.0-20150127051936-fcdddc395df1 h1:hUObRUqVLMQ2TEstLROK9Fw5+YWjkbzY48nXo+gLdLY=
+github.com/hashicorp/go-multierror v0.0.0-20150127051936-fcdddc395df1/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM=
+github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw=
+github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/memberlist v0.0.0-20171201184301-3d8438da9589 h1:V0VICRD0AA9PQmR1sqvSQyv8i3iUVI9rQtmM2YwzsAY=
+github.com/hashicorp/memberlist v0.0.0-20171201184301-3d8438da9589/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE=
+github.com/hashicorp/serf v0.0.0-20160317193612-598c54895cc5 h1:wjD77m26jcVP6sb2+EQ0YNxSCqmlMs3vibnDs4kFWok=
+github.com/hashicorp/serf v0.0.0-20160317193612-598c54895cc5/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
+github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
+github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed h1:3MJOWnAfq3T9eoCQTarEY2DMlUWYcBkBLf03dAMvEb8=
+github.com/ishidawataru/sctp v0.0.0-20180213033435-07191f837fed/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8=
+github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
+github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v0.0.0-20150511174710-5cf931ef8f76 h1:i5TIRQpbCg4aJMUtVHIhkQnSw++Z405Z5pzqHqeNkdU=
+github.com/kr/pty v0.0.0-20150511174710-5cf931ef8f76/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk=
+github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/matttproud/golang_protobuf_extensions v1.0.0 h1:YNOwxxSJzSUARoD9KRZLzM9Y858MNGCOACTvCW9TSAc=
+github.com/matttproud/golang_protobuf_extensions v1.0.0/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.7 h1:U73AZpKatVaVHVcWk9id1oW54af1NoXRRo+USzIh44Q=
+github.com/miekg/dns v1.0.7/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mistifyio/go-zfs v0.0.0-20160425201758-22c9b32c84eb h1:iTqJ2fjDnaldY7BXhfc15HkT769kWAstiz2bCmUrKAw=
+github.com/mistifyio/go-zfs v0.0.0-20160425201758-22c9b32c84eb/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
+github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 h1:hOY53G+kBFhbYFpRVxHl5eS7laP6B1+Cq+Z9Dry1iMU=
+github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
+github.com/moby/buildkit v0.0.0-20181210113810-d9f75920678e h1:hwBc7rfbhEycry18ndIWZwP9DTR9I+hBpFcoUExN6AI=
+github.com/moby/buildkit v0.0.0-20181210113810-d9f75920678e/go.mod h1:nnELdKPRkUAQR6pAB3mRU3+IlbqL3SSaAWqQL8k/K+4=
+github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
+github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
+github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
+github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/runc v0.0.0-20181203215513-96ec2177ae84 h1:UaOX8E48qzdqHAT5tJpaCueEFTDjuEIcVNzj1E9W6go=
+github.com/opencontainers/runc v0.0.0-20181203215513-96ec2177ae84/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opencontainers/runtime-spec v0.0.0-20180911193056-5684b8af48c1 h1:AcjCvgprf9I23wEYTHuyuHcuuQAp4hK5l+u1FUXgVaM=
+github.com/opencontainers/runtime-spec v0.0.0-20180911193056-5684b8af48c1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/selinux v0.0.0-20180628160156-b6fa367ed7f5 h1:gB9Q5rgyqOydWjwo1/qd0AEWa0C8MIl+MR8tklNaQ+w=
+github.com/opencontainers/selinux v0.0.0-20180628160156-b6fa367ed7f5/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
+github.com/opentracing-contrib/go-stdlib v0.0.0-20171029140428-b1a47cfbdd75 h1:EIdPB7oNWEV0cOQ7eIrdyKQfEV5XxO/fB/GrEQIk7J0=
+github.com/opentracing-contrib/go-stdlib v0.0.0-20171029140428-b1a47cfbdd75/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w=
+github.com/opentracing/opentracing-go v0.0.0-20171003133519-1361b9cd60be h1:vn0ruyYif1hUWDS2aEUdh6JGUfgK8gOOLpz/iTjb6pQ=
+github.com/opentracing/opentracing-go v0.0.0-20171003133519-1361b9cd60be/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/pborman/uuid v0.0.0-20160209185913-a97ce2ca70fa h1:l8VQbMdmwFH37kOOaWQ/cw24/u8AuBz5lUym13Wcu0Y=
+github.com/pborman/uuid v0.0.0-20160209185913-a97ce2ca70fa/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
+github.com/philhofer/fwd v0.0.0-20160129035939-98c11a7a6ec8 h1:jkUFVqrKRttbdDqkTrvOmHxfqIsJK0Oe2WGi1ACAE+M=
+github.com/philhofer/fwd v0.0.0-20160129035939-98c11a7a6ec8/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pivotal-golang/clock v0.0.0-20151018222946-3fd3c1944c59 h1:XP4PiZqEUVNnGAGflkEeh8V4uHq23In4pCLSh3M3VhU=
+github.com/pivotal-golang/clock v0.0.0-20151018222946-3fd3c1944c59/go.mod h1:+mfK55nFzC6GZpxsSI3MpI4NkRjsRCaVjf1o/QqUNkc=
+github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/prometheus/client_golang v0.0.0-20160802072246-52437c81da6b h1:FBSxD2pyjKYZonsSBJlP1d4uzYt7ei7s1gY4/lYLljg=
+github.com/prometheus/client_golang v0.0.0-20160802072246-52437c81da6b/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_model v0.0.0-20150212101744-fa8ad6fec335 h1:0E/5GnGmzoDCtmzTycjGDWW33H0UBmAhR0h+FC8hWLs=
+github.com/prometheus/client_model v0.0.0-20150212101744-fa8ad6fec335/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/common v0.0.0-20160801171955-ebdfc6da4652 h1:A/LzG6yfGZUKPD+rhVa5CAqQawH8KHuLlZgukD8dxLs=
+github.com/prometheus/common v0.0.0-20160801171955-ebdfc6da4652/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/procfs v0.0.0-20160411190841-abf152e5f3e9 h1:ex32PG6WhE5zviWS08vcXTwX2IkaH9zpeYZZvrmj3/U=
+github.com/prometheus/procfs v0.0.0-20160411190841-abf152e5f3e9/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/samuel/go-zookeeper v0.0.0-20150415181332-d0e0d8e11f31 h1:K6A6FPUqjrujk315iZJFr8CLsW7zGhhmZDgGbghmleo=
+github.com/samuel/go-zookeeper v0.0.0-20150415181332-d0e0d8e11f31/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/seccomp/libseccomp-golang v0.0.0-20160531183505-32f571b70023 h1:ZtrLs4RfZhyCYqamkjrwNSSAYhnRYqBSi8g17h2kars=
+github.com/seccomp/libseccomp-golang v0.0.0-20160531183505-32f571b70023/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
+github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
+github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
+github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b h1:UzwAjzrPQVJoxLfb26YI2WRrhD3g09ZHt9vAQckWiPY=
+github.com/syndtr/gocapability v0.0.0-20150716010906-2c00daeb6c3b/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck=
+github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
+github.com/tinylib/msgp v0.0.0-20171013044219-3b556c645408 h1:mmIW5ujw3XQ2+qYAcS9rNuo1zr4pU/PaIOj5AIH8ylg=
+github.com/tinylib/msgp v0.0.0-20171013044219-3b556c645408/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tonistiigi/fsutil v0.0.0-20181011223333-2862f6bc5ac9 h1:w/+9MapoT4VpbsZzMILdG896EygCj14r82Tv1J7JFpw=
+github.com/tonistiigi/fsutil v0.0.0-20181011223333-2862f6bc5ac9/go.mod h1:eden9dLzAAuNQ0L7whFr6/Mzgz6btsvQpUnxOOI+CCE=
+github.com/ugorji/go v0.0.0-20151028022000-f1f1a805ed36 h1:vKlfv8sKDcjM5WIkcAzl5CZkKB8pppsrdmqczMTuapo=
+github.com/ugorji/go v0.0.0-20151028022000-f1f1a805ed36/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
+github.com/vbatts/tar-split v0.11.0 h1:Vdj/+9462ZtnhhfQylOpk4xC4IqMui1JSgdIbucTOLw=
+github.com/vbatts/tar-split v0.11.0/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
+github.com/vdemeester/shakers v0.1.0 h1:K+n9sSyUCg2ywmZkv+3c7vsYZfivcfKhMh8kRxCrONM=
+github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LWiKsh9RZtAQ=
+github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e h1:f1yevOHP+Suqk0rVc13fIkzcLULJbyQcXDba2klljD0=
+github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
+github.com/vishvananda/netns v0.0.0-20150710222425-604eaf189ee8 h1:MmJ82dMUwQ+0jmeFg/iWripmd3D+GlYUu/CV2m7hJto=
+github.com/vishvananda/netns v0.0.0-20150710222425-604eaf189ee8/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
+go.etcd.io/bbolt v1.3.1-etcd.8 h1:6J7QAKqfFBGnU80KRnuQxfjjeE5xAGE/qB810I3FQHQ=
+go.etcd.io/bbolt v1.3.1-etcd.8/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.opencensus.io v0.11.0 h1:qa5S/qAruAewHX1WcNh68voHexDKOko8wKSPQLeKBvQ=
+go.opencensus.io v0.11.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/net v0.0.0-20180719180050-a680a1efc54d h1:i6RB+Qz1ug7TvJdY4zieRMpnLAtkHSHTOvApNXfLT4A=
+golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225 h1:kNX+jCowfMYzvlSvJu5pQWEmyWFrBXJ3PBy10xKMXK8=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/oauth2 v0.0.0-20180529203656-ec22f46f877b h1:nCwwlzLoBQhkY/S3CJ2CGAU4pYfR8+5/TPGEHT+p5Nk=
+golang.org/x/oauth2 v0.0.0-20180529203656-ec22f46f877b/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180715085529-ac767d655b30 h1:4bYUqrXBoiI7UFQeibUwFhvcHfaEeL75O3lOcZa964o=
+golang.org/x/sys v0.0.0-20180715085529-ac767d655b30/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+google.golang.org/api v0.0.0-20180530150455-de943baf05a0 h1:rogYvg930BsYTkmfVruIH4k3cWyIPVq5gfIVU3aukXU=
+google.golang.org/api v0.0.0-20180530150455-de943baf05a0/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180523212516-694d95ba50e6 h1:ml4nKEd5C1J2pyehhVA5c2ArnRce4GzIi+mFSIGneb4=
+google.golang.org/genproto v0.0.0-20180523212516-694d95ba50e6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.12.0 h1:Mm8atZtkT+P6R43n/dqNDWkPPu5BwRVu/1rJnJCeZH8=
+google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+gotest.tools v2.1.0+incompatible h1:5USw7CrJBYKqjg9R7QlA6jzqZKEAtvW82aNmsxxGPxw=
+gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-- 
2.19.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-03-17  1:21 [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Christian Stewart
                   ` (4 preceding siblings ...)
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 6/6] package/docker-engine: " Christian Stewart
@ 2019-03-27 16:50 ` Thomas Petazzoni
  2019-03-27 18:36   ` Christian Stewart
  2019-04-07 20:17 ` Thomas Petazzoni
  6 siblings, 1 reply; 20+ messages in thread
From: Thomas Petazzoni @ 2019-03-27 16:50 UTC (permalink / raw)
  To: buildroot

Hello Christian,

I've been trying to get my head around this topic, but I don't really
grasp why we're changing this. What is the advantage of implementing
this go module integration compared to what we're doing today ? Looking
at your patches 2/6 to 6/6 doesn't make the thing very appealing: we
need additional files (go.mod and go.sum) for each package using Go,
which were not needed before, and we have a 5k lines patch in
docker-cli.

On Sat, 16 Mar 2019 18:21:37 -0700
Christian Stewart <christian@paral.in> wrote:

> This commit moves from the GOPATH mechanism to the new GO111MODULE approach for
> Go based packages. Old Go packages (with the exception of docker-cli) will
> compile without changes (for example, mender, flanneld).

I don't understand this last sentence: your series is converting
docker-cli (which requires a huge patch), but not mender and flanneld.

> Cavets with the current implementation:
> 
>  - "make source" may not produce a valid output
>  - module downloading occurs ignoring the Buildroot download server
>  - module downloading occurs in a post-patch hook

If it has all those caveats, so why do we want this ?

> Go code uses a package-based imports system. Imports are
> relative to a root directory, previously called GOPATH.
> 
> import "github.com/docker/docker/pkg/myutils"
> 
> This would resolve to $GOPATH/src/github.com/docker/docker/pkg/myutils.
> 
> Buildroot packages previously used an additional feature in the Go tool which
> allows packages to avoid using GOPATH by "vendoring" dependencies - copying the
> code directly into the Git repository.
> 
> vendor/github.com/docker/docker/pkg/myutils

What does this path means ?

> Old packages that used the vendor/ approach remain compatible via inferring the
> root import path from the download URL if no go.mod is present.
> 
> All current Buildroot Go modules use "vendor" to avoid downloading dependencies.
> This requires that the Go projects added to Buildroot include all of their
> dependencies in their repositories.

So some upstream Go projects collect all their dependencies in their
Git repo, while some other upstream Go projects do not do that ? Just
for my understanding, could you show an example of two projects, one in
each situation ?

> It also does not allow us the opportunity to
> validate or adjust dependency versions when upgrading packages in Buildroot.

I'm sorry, but I don't understand what you mean here :-/

> A project can contain any number of go.mod files. A go.mod file is akin to
> Node's package.json file. It specifies all direct and indirect dependencies of
> all Go packages in the subtree below the file. The Go tool can manage this file
> automatically if desired, and specifies a required format. Go.mod additionally
> requires dependency versions to be explicitly specified. There is no semantic
> versioning or asterisk-based version specifiers.
> 
> module mymodule
> 
> require (
> github.com/aws/aws-sdk-go v1.17.12 // indirect
> github.com/blang/semver v3.5.2-0.20180723201105-3c1074078d32+incompatible
> )

Please add a sentence before dropping some example code. Something
like: "Here is an example go.mod file that shows ...".

> The Go tool creates a go.sum file next to the go.mod file. The go.sum
> file is akin to Node's package-shrinkwrap.json.
> 
> github.com/aws/aws-sdk-go v1.15.31/go.mod
> h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
> github.com/aws/aws-sdk-go v1.17.12
> h1:jMFwRUaM0LcfdenfvbDLePNoWSoCdOHqF4RCvSB4xNQ=
> github.com/aws/aws-sdk-go v1.17.12/go.mod
> h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
> github.com/blang/semver
> v3.5.2-0.20180723201105-3c1074078d32+incompatible
> h1:8fBbhRkI5/0ocLFbrhPgnGUm0ogc+Gko1cRodPWDKX4=
> github.com/blang/semver
> v3.5.2-0.20180723201105-3c1074078d32+incompatible/go.mod
> h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=

Ditto, please make it clear that this is an example of a go.sum file.

> The file contains import paths, versions, and file hashes for the download as
> well as the contained go.mod file.
> 
> With replace directives, it's possible to link together local copies of modules:
> 
> replace (
> k8s.io/gengo => ./staging/src/k8s.io/gengo
> k8s.io/kube-openapi => ./staging/src/k8s.io/kube-openapi
> )

How does this story of "replace directives" fits in the overall
picture ? You just give this information about "replace directives",
with no connection with the rest of the explanation.

> When GO111MODULE=on, all of the Go tool commands become "module-aware." GOPATH
> is no longer required. Running "go build" for example will fetch dependencies
> from the Internet, checksum them, and extract to a staging directory (defaulting
> currently to GOPATH/pkg/gomod or so). Imports are automatically resolved to the
> appropriately versioned staging directory.
> 
> There are various ways to control the download / extract behavior:
> 
>   The GOPROXY environment variable allows further control over the
>   download source. If GOPROXY is unset, is the empty string, or is the
>   string "direct", downloads use the default direct connection to
>   version control systems. Setting GOPROXY to "off" disallows
>   downloading modules from any source. Otherwise, GOPROXY is expected to
>   be the URL of a module proxy, in which case the go command will fetch
>   all modules from that proxy. No matter the source of the modules,
>   downloaded modules must match existing entries in go.sum...
> 
>   Even when downloading directly from version control systems, the go
>   command synthesizes explicit info, mod, and zip files and stores them
>   in its local cache, $GOPATH/pkg/mod/cache/download, the same as if it
>   had downloaded them directly from a proxy. The cache layout is the
>   same as the proxy URL space, so serving $GOPATH/pkg/mod/cache/download
>   at (or copying it to) https://example.com/proxy would let other users
>   access those cached module versions with
>   GOPROXY=https://example.com/proxy.
> 
> GOPROXY additionally supports file:// URLs.

Perhaps this should be concluded by "Buildroot will set GOPROXY to ...
in order to achieve a behavior that ...".

> This commit sets GOPATH to $(DL_DIR)/go-module, as the Go module system will
> download and cache code sources in the GOPATH/pkg/mod path.
> 
> The go.mod and go.sum files can optionally be placed in $(2)_PKGDIR next to
> Config.in and other support files.

How does it work if there is no go.mod/go.sum file ?

> They are copied in with a post-download hook, and "go mod download"
> is executed to pull the dependencies from the internet.
> 
> A hook is added to execute "go mod vendor".

What is "go mod vendor" going to do ?

> Upstream vendor trees are still optionally supported, but can be
> overridden by placing go.mod into the Buildroot tree as described
> above. Package developers can alternatively specify LIBFOO_GOMOD to
> indicate the root module path. This allows the Go module tool to
> compile code using a legacy vendor/ tree, without a GOPATH to
> indicate the import path for the root module.
> 
> DOCKER_ENGINE_GOMOD = github.com/docker/docker

Again, please don't throw example code in the middle of the commit log
without an introduction.

> A Buildroot user can serve the dl/go-modules directory directly with
> a HTTP server and set GOPROXY such that all module downloads come
> from that server. This could be configurable eventually via the
> Buildroot KConfig architecture.
> 
> During the build phase, the "-mod=vendor" option is used to indicate
> that the extracted vendor/ tree (from the post-extract step) is to be
> used.
> 
> Go modules are never disabled with this approach. They are compatible
> with the legacy vendor/ tree approach by the above mechanisms.

These last two paragraphs are still very fuzzy for me :-/ I guess I
need to read more about Go modules.

Thanks!

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-03-27 16:50 ` [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Thomas Petazzoni
@ 2019-03-27 18:36   ` Christian Stewart
  2019-04-05  8:32     ` Arnout Vandecappelle
  0 siblings, 1 reply; 20+ messages in thread
From: Christian Stewart @ 2019-03-27 18:36 UTC (permalink / raw)
  To: buildroot

Hi Thomas,

Some initial clarifications: this is a RFC and very much a Work in
Progress. It is not complete, nor is it intended to be merged anywhere
near its current form. The patch series is a reply to Arnout's request
for an example of what these approaches might look like in Buildroot.

The original discussion is here:

https://patchwork.ozlabs.org/patch/1048076/

Thomas Petazzoni <thomas.petazzoni@bootlin.com> writes:
>> Cavets with the current implementation:
>>
>>  - "make source" may not produce a valid output
>>  - module downloading occurs ignoring the Buildroot download server
>>  - module downloading occurs in a post-patch hook
>
> If it has all those caveats, so why do we want this ?

Because these are issues with the initial WIP/RFC. Please note the last
email in the chain at https://patchwork.ozlabs.org/patch/1048076/

Arnout:

  So these are the more controversial ones. I think you should pick the
  one you like most, make a quick prototype that works for one specific
  package, note what is missing in the commit log, and send it to the
  list for discussion.

So here you go. This is the prototype with the list of cavets of the
prototype. This is not a list of cavets with Go modules in general.

The introduction to the commit message is a copy of my writeup on what
Go modules are.

> I've been trying to get my head around this topic, but I don't really
> grasp why we're changing this. What is the advantage of implementing
> this go module integration compared to what we're doing today ?

The "old way" using GOPATH will be removed in Go 1.13 or Go 1.14. We
need to plan a solution in advance. However, vendor/ will not stop
working anytime soon.

The most minimal version of this patch series would be to enable
GO111MODULE, place a go.mod file which specifies the Go import path of
the root of the repository, and allow the Go tool to use the upstream
vendor/ tree. The patch already does this in the absence of
$(PKG_DIR)/go.mod and $(@D)/go.mod with the presence of $(@D)/vendor.

Today, to accomplish the same behavior with GOPATH (which is being
removed/deprecated in Go 1.13), we symlink a directory path like this:

$(@D)/gopath/src/github.com/docker/docker/ -> $(@D)

We currently require upstream developers to copy the source code of
every dependency into their repository under vendor/. For each Go
package we get a separate dependency tree provided by the upstream
developer at the version we have the package pinned to in Buildroot.

> Looking at your patches 2/6 to 6/6 doesn't make the thing very
> appealing: we need additional files (go.mod and go.sum) for each
> package using Go, which were not needed before, and we have a 5k lines
> patch in docker-cli.

The vendor/ mechanism continues to work, however, not every Go package
copies dependencies verbatim into their repository. Under the "current
way" to support these vendor-less packages, I would need to submit a
patch to buildroot that contains a copy of every dependency's Go source.

> I don't understand this last sentence: your series is converting
> docker-cli (which requires a huge patch), but not mender and flanneld.

Mender and Flanneld are examples of packages with vendor/ trees that are
still compatible with the new patch. Docker-cli is an example of how we
might override the upstream vendor/ tree with our own dependency
pinning. (the go.mod and go.sum files produce the same vendor/ tree as
upstream has in their source tree today).

This is a Proof-of-Concept of how we might update a specific dependency
with a critical CVE or other bug which is vendored into an old upstream
vendor/ tree. The Buildroot maintainers, upon discovering the CVE or
other bug, could update the specific dependency to a newer revision with
the fix. This a "feature" and more speculative. It's not required for a
v1 of this patch.

>> Buildroot packages previously used an additional feature in the Go tool which
>> allows packages to avoid using GOPATH by "vendoring" dependencies - copying the
>> code directly into the Git repository.
>>
>> vendor/github.com/docker/docker/pkg/myutils
>
> What does this path means ?

It's an example of the sentence immediately before the line. This patch
series is intended to be a initial introduction and a example of what
something like this might look like. The commit message is not intended
to be final yet. I will adjust anything I write in the future to
explicitly say "here is an example..." instead of assuming that the
reader is looking top-to-bottom.

> So some upstream Go projects collect all their dependencies in their
> Git repo, while some other upstream Go projects do not do that ? Just
> for my understanding, could you show an example of two projects, one
> in each situation ?

Yes, this is correct.

Most major projects still use vendor/ today because it's the most
foolproof way (without Go modules) to ensure no incorrect dependency
gets into the mix, and because most projects are still supporting Go
1.10 which does not have Go module support.

Go modules were just added in Go 1.11 (we're at 1.12.1 now).

Here's a background on vendor:

https://github.com/golang/go/wiki/PackageManagementTools#go15vendorexperiment

Here's a background on Go modules:

https://github.com/golang/go/wiki/Modules

Docker currently still collects vendor tree. Here is a URL to that:

https://github.com/docker/engine/tree/da823cf3a5a3437763ba12b2fa369c826d4a2bf4/vendor

Here are some examples of projects that do not include vendor/:

 - https://github.com/dustin/gomemcached
 - https://github.com/syncthing/syncthing
 - https://github.com/drone/drone
 - https://github.com/nsqio/nsq
 - https://github.com/cockroachdb/cockroach

CockroachDB is an interesting example of a common case where vendor/
will NOT work with Buildroot. The vendor/ tree is a submodule. As we
currently download the GitHub tarball for GitHub based packages, this
source tarball will not contain the contents of vendor/.

However, the project does have Gopkg.lock and Gopkg.toml, which are
files for an old tool called "Dep" which was previously created by the
community to solve dependency versioning. The Go Modules tool will
automatically read and recognize this format and convert it to go.mod.

With this patch, you could add CockroachDB and the other projects above.
Without, it would not be able to be added, and I don't see any way to
get the submodule into the source tree, so it probably will never be
added to Buildroot.

>> It also does not allow us the opportunity to validate or adjust
>> dependency versions when upgrading packages in Buildroot.
>
> I'm sorry, but I don't understand what you mean here :-/

Please see my above comments re: updating a dependency version when a
CVE or other bug is found. Specifically, consider the case where we have
an older LTS Buildroot branch pointing to an unmaintained older revision
of the project. With vendor/ coming from the downloaded upstream source
tarball, we would not be able to fix the CVE or bug without copying the
patch in the Buildroot tree.

>> A project can contain any number of go.mod files. A go.mod file is akin to
>> Node's package.json file. It specifies all direct and indirect dependencies of
>> all Go packages in the subtree below the file. The Go tool can manage this file
>> automatically if desired, and specifies a required format. Go.mod additionally
>> requires dependency versions to be explicitly specified. There is no semantic
>> versioning or asterisk-based version specifiers.
>>
>> module mymodule
>>
>> require (
>> github.com/aws/aws-sdk-go v1.17.12 // indirect
>> github.com/blang/semver v3.5.2-0.20180723201105-3c1074078d32+incompatible
>> )
>
> Please add a sentence before dropping some example code. Something
> like: "Here is an example go.mod file that shows ...".

The entire paragraph before the code sample explains what it is. I will
explicitly label it as an example in the future.

>> replace (
>> k8s.io/gengo => ./staging/src/k8s.io/gengo
>> k8s.io/kube-openapi => ./staging/src/k8s.io/kube-openapi
>> )
>
> How does this story of "replace directives" fits in the overall
> picture ? You just give this information about "replace directives",
> with no connection with the rest of the explanation.

These can be placed into go.mod and allow you to replace a specific
dependency with another one, or to alias the dependency to a local path.

> Perhaps this should be concluded by "Buildroot will set GOPROXY to ...
> in order to achieve a behavior that ...".

Please note the commit message is a copy/paste of my Go modules
explanation from the email thread in the original discussion. I have no
idea how we would address GOPROXY in a v1 of this patch.

>> This commit sets GOPATH to $(DL_DIR)/go-module, as the Go module system will
>> download and cache code sources in the GOPATH/pkg/mod path.
>>
>> The go.mod and go.sum files can optionally be placed in $(2)_PKGDIR next to
>> Config.in and other support files.
>
> How does it work if there is no go.mod/go.sum file ?

We place a go.mod file with a single line, for example:

 module github.com/docker/docker

This is sufficient to use the upstream vendor/ tree. The Go tool can
also read the package files from other tools like Dep (Gopkg.toml).

>> They are copied in with a post-download hook, and "go mod download"
>> is executed to pull the dependencies from the internet.
>>
>> A hook is added to execute "go mod vendor".
>
> What is "go mod vendor" going to do ?

It will extract the files from the buildroot go-module download tree
into $(@D)/vendor/...

>> Upstream vendor trees are still optionally supported, but can be
>> overridden by placing go.mod into the Buildroot tree as described
>> above. Package developers can alternatively specify LIBFOO_GOMOD to
>> indicate the root module path. This allows the Go module tool to
>> compile code using a legacy vendor/ tree, without a GOPATH to
>> indicate the import path for the root module.
>>
>> DOCKER_ENGINE_GOMOD = github.com/docker/docker
>
> Again, please don't throw example code in the middle of the commit log
> without an introduction.

Please see above.

>> A Buildroot user can serve the dl/go-modules directory directly with
>> a HTTP server and set GOPROXY such that all module downloads come
>> from that server. This could be configurable eventually via the
>> Buildroot KConfig architecture.
>>
>> During the build phase, the "-mod=vendor" option is used to indicate
>> that the extracted vendor/ tree (from the post-extract step) is to be
>> used.
>
> These last two paragraphs are still very fuzzy for me :-/ I guess I
> need to read more about Go modules.

 1. Code is downloaded by the Go tool, hashed, and stored in dl/go-modules.
 2. Code is extracted to $(@D)/vendor with the "go mod vendor" command.
 3. go build -mod=vendor ./ will inform the Go tool that we want to use vendor/

Best regards,
Christian Stewart

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-03-27 18:36   ` Christian Stewart
@ 2019-04-05  8:32     ` Arnout Vandecappelle
  2019-04-05 10:47       ` Christian Stewart
  0 siblings, 1 reply; 20+ messages in thread
From: Arnout Vandecappelle @ 2019-04-05  8:32 UTC (permalink / raw)
  To: buildroot

 Hi Christian,

 Thanks again for working on this. And thanks Thomas for taking a look as well.

On 27/03/2019 19:36, Christian Stewart wrote:
> Hi Thomas,
> 
> Some initial clarifications: this is a RFC and very much a Work in
> Progress. It is not complete, nor is it intended to be merged anywhere
> near its current form. The patch series is a reply to Arnout's request
> for an example of what these approaches might look like in Buildroot.

 You should have put this sentence prominently in the first patch's commit
message. But the RFC should have been a hint, of course. Also, I've marked the
series as RFC in patchwork.


 I think that one issue with this gomodules stuff is that the mails (and commit
messages) are getting too long, which makes it very hard for people to
understand it all. Of course, it doesn't help that all this go stuff is wildly
unfamiliar for most people.

 But anyway, it would be good if parts of this problem could be bitten off in
smaller pieces. Suggestions will follow.


> The original discussion is here:
> 
> https://patchwork.ozlabs.org/patch/1048076/
> 
> Thomas Petazzoni <thomas.petazzoni@bootlin.com> writes:
>>> Cavets with the current implementation:
>>>
>>>  - "make source" may not produce a valid output
>>>  - module downloading occurs ignoring the Buildroot download server
>>>  - module downloading occurs in a post-patch hook
>>
>> If it has all those caveats, so why do we want this ?
> 
> Because these are issues with the initial WIP/RFC.

 Still, it would be good if you had at least some idea of how these could be solved.


> Please note the last
> email in the chain at https://patchwork.ozlabs.org/patch/1048076/
> 
> Arnout:
> 
>   So these are the more controversial ones. I think you should pick the
>   one you like most, make a quick prototype that works for one specific
>   package, note what is missing in the commit log, and send it to the
>   list for discussion.

 You forgot to mention the two options, and which one you picked:

   - post-extract hook with optional go.mod in repository, "go mod vendor"
   - additional download method which does "go mod vendor" and re-compress to
     a tarball (similar to git download)

and IIUC you picked the first one.


> So here you go. This is the prototype with the list of cavets of the
> prototype. This is not a list of cavets with Go modules in general.
> 
> The introduction to the commit message is a copy of my writeup on what
> Go modules are.
> 
>> I've been trying to get my head around this topic, but I don't really
>> grasp why we're changing this. What is the advantage of implementing
>> this go module integration compared to what we're doing today ?
> 
> The "old way" using GOPATH will be removed in Go 1.13 or Go 1.14. We
> need to plan a solution in advance. However, vendor/ will not stop
> working anytime soon.

 So, here's a bite-sized piece: just convert GOPATH into GO111MODULE, but
assuming that the vendor/ tree is present. I believe that the current patch does
exactly that, but it *also* does something else...

> 
> The most minimal version of this patch series would be to enable
> GO111MODULE, place a go.mod file which specifies the Go import path of

 ... I understood from our previous conversation that the go.mod file is in fact
not needed. If the go.mod file is missing, then the go tool will just use the
vendor tree as is. Which is perfect for our current situation.

 So, IMO, the _APPLY_EXTRACT_GOMOD part should be removed from this patch. Then,
patch 1 becomes a really simple patch that people can get their head around. And
you get a much better baseline to explain what you want to do, since you no
longer have to compare against GOPATH; you only need to compare against a
non-instantiated vendor tree.

 As usual, if I understand correctly, of course :-)


> the root of the repository, and allow the Go tool to use the upstream
> vendor/ tree. The patch already does this in the absence of
> $(PKG_DIR)/go.mod and $(@D)/go.mod with the presence of $(@D)/vendor.
> 
> Today, to accomplish the same behavior with GOPATH (which is being
> removed/deprecated in Go 1.13), we symlink a directory path like this:
> 
> $(@D)/gopath/src/github.com/docker/docker/ -> $(@D)
> 
> We currently require upstream developers to copy the source code of
> every dependency into their repository under vendor/. For each Go
> package we get a separate dependency tree provided by the upstream
> developer at the version we have the package pinned to in Buildroot.
> 
>> Looking at your patches 2/6 to 6/6 doesn't make the thing very
>> appealing: we need additional files (go.mod and go.sum) for each
>> package using Go, which were not needed before, and we have a 5k lines
>> patch in docker-cli.
> 
> The vendor/ mechanism continues to work, however, not every Go package
> copies dependencies verbatim into their repository. Under the "current
> way" to support these vendor-less packages, I would need to submit a
> patch to buildroot that contains a copy of every dependency's Go source.

 Actually, you would need to specify additional downloads, and a post-extract
hook to extract them into the vendor directory.

 If you can give an example of a package that only has a few of these, I could
create an example of how it might work without any extra infra. Although I'm not
entirely sure if such an example would be very useful, except to show that it
would be extremely hard to maintain.


>> I don't understand this last sentence: your series is converting
>> docker-cli (which requires a huge patch), but not mender and flanneld.
> 
> Mender and Flanneld are examples of packages with vendor/ trees that are
> still compatible with the new patch. Docker-cli is an example of how we
> might override the upstream vendor/ tree with our own dependency
> pinning. (the go.mod and go.sum files produce the same vendor/ tree as
> upstream has in their source tree today).

 If that is the example, then it shows that it's definitely not the way we want
to go... But I don't think it's the example that you want to show, because
you're not yet using go modules to download stuff.


> This is a Proof-of-Concept of how we might update a specific dependency
> with a critical CVE or other bug which is vendored into an old upstream
> vendor/ tree. The Buildroot maintainers, upon discovering the CVE or
> other bug, could update the specific dependency to a newer revision with
> the fix. This a "feature" and more speculative. It's not required for a
> v1 of this patch.

 Is this something that we want to do? Go has apparently chosen for this
vendoring model where it is the responsibility of the package maintainers to
update their dependencies, and not of the distro. In that model, if the package
maintainers don't do their job, we're not going to fix the world for them. In
general, we only take fixes (even for CVEs) that come from upstream. Maybe we'll
backport a fix, but nothing more than that.

 By using the vendoring approach, Go has basically chosen to make each CVE in a
library a CVE against all packages using that library, which has to be solved
separately in each user. Recursively.

 All of the above IMHO.

[snip]
> Here are some examples of projects that do not include vendor/:
> 
>  - https://github.com/dustin/gomemcached
>  - https://github.com/syncthing/syncthing
>  - https://github.com/drone/drone
>  - https://github.com/nsqio/nsq
>  - https://github.com/cockroachdb/cockroach
> 
> CockroachDB is an interesting example of a common case where vendor/
> will NOT work with Buildroot. The vendor/ tree is a submodule. As we
> currently download the GitHub tarball for GitHub based packages, this
> source tarball will not contain the contents of vendor/.

 That one would actually still work - we *can* use the git download method for
github packages.

 nsq is a better example - it doesn't have a vendor tree nor submodules. And it
has an actual modern go.mod. And it's a nice example that trying to do this
manually within buildroot is not doable: the go.mod is huge. Like, npm-huge...

 However, the current patch set does nothing to solve that, I think. So it would
be nice to have an example of how we might integrate something like nsq.


> However, the project does have Gopkg.lock and Gopkg.toml, which are
> files for an old tool called "Dep" which was previously created by the
> community to solve dependency versioning. The Go Modules tool will
> automatically read and recognize this format and convert it to go.mod.
> 
> With this patch, you could add CockroachDB and the other projects above.
> Without, it would not be able to be added, and I don't see any way to
> get the submodule into the source tree, so it probably will never be
> added to Buildroot.
> 
>>> It also does not allow us the opportunity to validate or adjust
>>> dependency versions when upgrading packages in Buildroot.
>>
>> I'm sorry, but I don't understand what you mean here :-/
> 
> Please see my above comments re: updating a dependency version when a
> CVE or other bug is found.

 As I explained above: Buildroot will not do that.

> Specifically, consider the case where we have
> an older LTS Buildroot branch pointing to an unmaintained older revision
> of the project. With vendor/ coming from the downloaded upstream source
> tarball, we would not be able to fix the CVE or bug without copying the
> patch in the Buildroot tree.

 Well, actually we can... In any of the approaches we're considering, after the
extract step we will have all the dependencies present in the vendor/ tree. So
we can always add a patch for a CVE fix in a dependent package in the usual way
(we just have to add some directory components to the upstream patch).

 The only situation where this would not be the case is if we would let the go
tool do its downloads during the build step. But that is something we
*definitely* want to avoid.


>>> A project can contain any number of go.mod files. A go.mod file is akin to
>>> Node's package.json file. It specifies all direct and indirect dependencies of
>>> all Go packages in the subtree below the file. The Go tool can manage this file
>>> automatically if desired, and specifies a required format. Go.mod additionally
>>> requires dependency versions to be explicitly specified. There is no semantic
>>> versioning or asterisk-based version specifiers.
>>>
>>> module mymodule
>>>
>>> require (
>>> github.com/aws/aws-sdk-go v1.17.12 // indirect
>>> github.com/blang/semver v3.5.2-0.20180723201105-3c1074078d32+incompatible
>>> )
>>
>> Please add a sentence before dropping some example code. Something
>> like: "Here is an example go.mod file that shows ...".
> 
> The entire paragraph before the code sample explains what it is. I will
> explicitly label it as an example in the future.
> 
>>> replace (
>>> k8s.io/gengo => ./staging/src/k8s.io/gengo
>>> k8s.io/kube-openapi => ./staging/src/k8s.io/kube-openapi
>>> )
>>
>> How does this story of "replace directives" fits in the overall
>> picture ? You just give this information about "replace directives",
>> with no connection with the rest of the explanation.
> 
> These can be placed into go.mod and allow you to replace a specific
> dependency with another one, or to alias the dependency to a local path.

 Yeah, that bit is not very clear to me either... Is it intended to be able to
override the version of recursive dependencies?


>> Perhaps this should be concluded by "Buildroot will set GOPROXY to ...
>> in order to achieve a behavior that ...".
> 
> Please note the commit message is a copy/paste of my Go modules
> explanation from the email thread in the original discussion. I have no
> idea how we would address GOPROXY in a v1 of this patch.

 Easy: in the current sitatution, we *never* want the go tool to download
anything (because we expect everything to be present in the vendor/ tree), so we
can just globally export GOPROXY=off. IIUC.

 Note that this is something that would potentially break existing external
packages, so should be mentioned clearly in the release notes. It can be fixed
easily, of course, by setting GOPROXY=direct in <PKG>_GO_ENV.

 Given that this is a slightly more controversial thing, I would do it in a
separate patch from the GOPATH removal.


>>> This commit sets GOPATH to $(DL_DIR)/go-module, as the Go module system will
>>> download and cache code sources in the GOPATH/pkg/mod path.
>>>
>>> The go.mod and go.sum files can optionally be placed in $(2)_PKGDIR next to
>>> Config.in and other support files.
>>
>> How does it work if there is no go.mod/go.sum file ?
> 
> We place a go.mod file with a single line, for example:
> 
>  module github.com/docker/docker
> 
> This is sufficient to use the upstream vendor/ tree. The Go tool can
> also read the package files from other tools like Dep (Gopkg.toml).

 So,

- if a go.mod is present, we can use the Go tool;
- if a legacy .toml or whatever is present, we can use the Go tool;
- if there is a vendor/ tree but no metadata, we need to create an almost-empty
  go.mod.

Correct?

 And all of that is only needed to be able to do the 'go mod vendor' command in
a post-extract hook, right?

 What does 'go mod vendor' do if there is no go.mod or legacy file?

 What I also don't understand is why we would ever add a go.mod ourselves... If
upstream has one, clearly there is no reason to add one. If upstream has a
.toml, then we can just use that. If upstream has nothing, it means that
everything is already in the vendor/ tree so implicitly versioned, so we also
don't need anything from the go mod logic. We *maybe* should create the one-line
go.mod that just declares the module, but even that I'm not sure of.

 Can you explain why patch 2 is needed, converting runc's vendor.conf into a
go.mod and go.sum?


>>> They are copied in with a post-download hook, and "go mod download"
>>> is executed to pull the dependencies from the internet.

 I may be blind, but I don't see this call to 'go mod download'... I suppose
that it is in fact the 'go mod vendor' call that will implicitly do a download, no?


>>> A hook is added to execute "go mod vendor".
>>
>> What is "go mod vendor" going to do ?
> 
> It will extract the files from the buildroot go-module download tree
> into $(@D)/vendor/...
> 
>>> Upstream vendor trees are still optionally supported, but can be
>>> overridden by placing go.mod into the Buildroot tree as described
>>> above. Package developers can alternatively specify LIBFOO_GOMOD to
>>> indicate the root module path. This allows the Go module tool to
>>> compile code using a legacy vendor/ tree, without a GOPATH to
>>> indicate the import path for the root module.
>>>
>>> DOCKER_ENGINE_GOMOD = github.com/docker/docker
>>
>> Again, please don't throw example code in the middle of the commit log
>> without an introduction.
> 
> Please see above.
> 
>>> A Buildroot user can serve the dl/go-modules directory directly with
>>> a HTTP server and set GOPROXY such that all module downloads come
>>> from that server. This could be configurable eventually via the
>>> Buildroot KConfig architecture.
>>>
>>> During the build phase, the "-mod=vendor" option is used to indicate
>>> that the extracted vendor/ tree (from the post-extract step) is to be
>>> used.
>>
>> These last two paragraphs are still very fuzzy for me :-/ I guess I
>> need to read more about Go modules.
> 
>  1. Code is downloaded by the Go tool, hashed, and stored in dl/go-modules.
>  2. Code is extracted to $(@D)/vendor with the "go mod vendor" command.
>  3. go build -mod=vendor ./ will inform the Go tool that we want to use vendor/

 So, I think this should be chunked into:

1. Replace GOPATH with -mod=vendor - the same patch will need to update packages
if needed to keep them building.

2. Update packages to remove no-longer-needed _WORKSPACE etc. settings.

3. export GOPROXY=off

4. Run 'go mod vendor' in a post-extract hook. This should still be RFC because
it violates the offline builds principle. Don't forget to mention some ideas of
how the limitations could be overcome.

5. Add a package (nsq?) that would be very difficult to support without the
go.mod support.

6. Add support for Buildroot-supplied go.mod.

7. Add or modify a package that actually makes sensible use of the
Buildroot-supplied go.mod.


 Regards,
 Arnout

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 3/6] package/docker-containerd: upgrade to go modules
  2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 3/6] package/docker-containerd: " Christian Stewart
@ 2019-04-05  8:36   ` Arnout Vandecappelle
  2019-04-05 10:58     ` Christian Stewart
  0 siblings, 1 reply; 20+ messages in thread
From: Arnout Vandecappelle @ 2019-04-05  8:36 UTC (permalink / raw)
  To: buildroot



On 17/03/2019 02:21, Christian Stewart wrote:
>  DOCKER_CONTAINERD_LDFLAGS = \
> -	-X github.com/docker/containerd.GitCommit=$(DOCKER_CONTAINERD_VERSION)
> +	-X github.com/containerd/containerd.GitCommit=$(DOCKER_CONTAINERD_VERSION)

 This looks like a completely independent change. Or is it because we now have a
go.mod that calls the module containerd/containerd instead of docker/containerd?


> -DOCKER_CONTAINERD_BUILD_TARGETS = cmd/ctr cmd/containerd cmd/containerd-shim
> +DOCKER_CONTAINERD_BUILD_TARGETS = \
> +	github.com/containerd/containerd/cmd/ctr \
> +	github.com/containerd/containerd/cmd/containerd \
> +	github.com/containerd/containerd/cmd/containerd-shim

 Same question, how is this related to the go modules?

 Would it be possible (and useful) to add those prefixes automatically?

 Regards,
 Arnout

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-04-05  8:32     ` Arnout Vandecappelle
@ 2019-04-05 10:47       ` Christian Stewart
  2019-04-05 14:07         ` Arnout Vandecappelle
  0 siblings, 1 reply; 20+ messages in thread
From: Christian Stewart @ 2019-04-05 10:47 UTC (permalink / raw)
  To: buildroot

Hi Arnout,


Arnout Vandecappelle <arnout@mind.be> writes:
>>>> Cavets with the current implementation:
>>>>
>>>>  - "make source" may not produce a valid output
>>>>  - module downloading occurs ignoring the Buildroot download server
>>>>  - module downloading occurs in a post-patch hook
>>> If it has all those caveats, so why do we want this ?
>> Because these are issues with the initial WIP/RFC.
>  Still, it would be good if you had at least some idea of how these could be solved.

Create a new package download method that uses the "go mod download" and
"go mod vendor" steps seems to be the only way to go.

>  So, here's a bite-sized piece: just convert GOPATH into GO111MODULE, but
> assuming that the vendor/ tree is present. I believe that the current patch does
> exactly that, but it *also* does something else...
>> 
>> The most minimal version of this patch series would be to enable
>> GO111MODULE, place a go.mod file which specifies the Go import path of
>
>  ... I understood from our previous conversation that the go.mod file is in fact
> not needed. If the go.mod file is missing, then the go tool will just use the
> vendor tree as is. Which is perfect for our current situation.

The file IS needed because the Go tool needs to know the root package
path at $(@D). For example, in Docker, this is
"github.com/docker/docker" even though the code comes from
"github.com/docker/engine."

>
>  So, IMO, the _APPLY_EXTRACT_GOMOD part should be removed from this patch. Then,
> patch 1 becomes a really simple patch that people can get their head around. And
> you get a much better baseline to explain what you want to do, since you no
> longer have to compare against GOPATH; you only need to compare against a
> non-instantiated vendor tree.

This part cannot be removed but the go.mod in the Buildroot tree and the
module download / vendor steps could be for an initial version. I'll
spin a series with this soon.

>  Actually, you would need to specify additional downloads, and a post-extract
> hook to extract them into the vendor directory.
>
>  If you can give an example of a package that only has a few of these, I could
> create an example of how it might work without any extra infra. Although I'm not
> entirely sure if such an example would be very useful, except to show that it
> would be extremely hard to maintain.

This doesn't make any sense to me.

>  If that is the example, then it shows that it's definitely not the way we want
> to go... But I don't think it's the example that you want to show, because
> you're not yet using go modules to download stuff.

I am using Go modules to download stuff in this PoC patch.

>> This is a Proof-of-Concept of how we might update a specific dependency
>> with a critical CVE or other bug which is vendored into an old upstream
>> vendor/ tree. The Buildroot maintainers, upon discovering the CVE or
>> other bug, could update the specific dependency to a newer revision with
>> the fix. This a "feature" and more speculative. It's not required for a
>> v1 of this patch.
>
>  Is this something that we want to do? Go has apparently chosen for this
> vendoring model where it is the responsibility of the package maintainers to
> update their dependencies, and not of the distro. In that model, if the package
> maintainers don't do their job, we're not going to fix the world for them. In
> general, we only take fixes (even for CVEs) that come from upstream. Maybe we'll
> backport a fix, but nothing more than that.

I don't see the point. If we discover a security issue, this provides a
mechanism to override a dependency at download time. If this is not
something that Buildroot wants I'm happy to just remove the 2 lines it
takes to implement it in the code.

>  By using the vendoring approach, Go has basically chosen to make each CVE in a
> library a CVE against all packages using that library, which has to be solved
> separately in each user. Recursively.

They have not chosen to do this. The vendoring model is/was a stop-gap
until something better (Modules) was developed.

>  nsq is a better example - it doesn't have a vendor tree nor submodules. And it
> has an actual modern go.mod. And it's a nice example that trying to do this
> manually within buildroot is not doable: the go.mod is huge. Like, npm-huge...
>
>  However, the current patch set does nothing to solve that, I think. So it would
> be nice to have an example of how we might integrate something like nsq.

The current patch set DOES solve this. It would work fine AS IS, since
the "go mod vendor" step downloads and extracts dependencies from go.mod.

The only awkward part of the RFC is that this behavior is not placed
into a proper download mechanism rather than the extract step.

>>>> It also does not allow us the opportunity to validate or adjust
>>>> dependency versions when upgrading packages in Buildroot.
>>
>> Please see my above comments re: updating a dependency version when a
>> CVE or other bug is found.
>
>  As I explained above: Buildroot will not do that.

Why?

>> Specifically, consider the case where we have
>> an older LTS Buildroot branch pointing to an unmaintained older revision
>> of the project. With vendor/ coming from the downloaded upstream source
>> tarball, we would not be able to fix the CVE or bug without copying the
>> patch in the Buildroot tree.
>
>  Well, actually we can... In any of the approaches we're considering, after the
> extract step we will have all the dependencies present in the vendor/ tree. So
> we can always add a patch for a CVE fix in a dependent package in the usual way
> (we just have to add some directory components to the upstream patch).

I don't see why this is better than overriding go.mod?

>  The only situation where this would not be the case is if we would let the go
> tool do its downloads during the build step. But that is something we
> *definitely* want to avoid.

This doesn't make any sense to me either. Who proposed downloading
during the build step? Why would this be the only case where adding a
patch would not work? With a new download method, it would go:

 1. Download base package (containing go.mod)
 2. (optional) Copy in go.mod and go.sum from Buildroot tree
 3. Execute "go mod vendor." Note, go.sum ensures the result is consistent.
 4. Compress the result and store in dl/ tree.
 5. Later, in the extract step, extract this tarball
 6. Apply any patches (this can also target the vendor/ tree if needed)
 7. Build the package with -mod=vendor arg which uses the vendor/ tree.

>>>> replace (
>>>> k8s.io/gengo => ./staging/src/k8s.io/gengo
>>>> k8s.io/kube-openapi => ./staging/src/k8s.io/kube-openapi
>>>> )
>>>
>>> How does this story of "replace directives" fits in the overall
>>> picture ? You just give this information about "replace directives",
>>> with no connection with the rest of the explanation.
>> 
>> These can be placed into go.mod and allow you to replace a specific
>> dependency with another one, or to alias the dependency to a local path.
>
>  Yeah, that bit is not very clear to me either... Is it intended to be able to
> override the version of recursive dependencies?

Yes. You can also tell Go to use local copies of dependencies out-of-tree.

>  Easy: in the current sitatution, we *never* want the go tool to download
> anything (because we expect everything to be present in the vendor/ tree), so we
> can just globally export GOPROXY=off. IIUC.

This is already done in the above patch, with the exception of the "go
mod vendor" step which does a download, which would be moved into a
download provider in the actual change.

>  Note that this is something that would potentially break existing external
> packages, so should be mentioned clearly in the release notes. It can be fixed
> easily, of course, by setting GOPROXY=direct in <PKG>_GO_ENV.

How would this break external packages? The _GOMOD variable is
automatically determined using the same logic that the GOPATH directory
structure is determined today. This means that existing external
packages using vendor/ would still build fine.

>  Given that this is a slightly more controversial thing, I would do it in a
> separate patch from the GOPATH removal.

I'm not sure what specifically you're referring to here.

>  So,
>
> - if a go.mod is present, we can use the Go tool;
> - if a legacy .toml or whatever is present, we can use the Go tool;
> - if there is a vendor/ tree but no metadata, we need to create an almost-empty
>   go.mod.
>
> Correct?

Yes, the go.mod needs to contain a single line to inform the Go tool of
the package path of the root of the code.

>  And all of that is only needed to be able to do the 'go mod vendor' command in
> a post-extract hook, right?

No, this is needed to get Go to use the vendor/ tree without a GOPATH,
because it used to derive the root package path of the code from the
location in the GOPATH tree.

>  What does 'go mod vendor' do if there is no go.mod or legacy file?

It fails.

>  What I also don't understand is why we would ever add a go.mod ourselves... If
> upstream has one, clearly there is no reason to add one.

Upstream does not have one in a lot of cases (yet).

> If upstream has a .toml, then we can just use that. If upstream has
> nothing, it means that everything is already in the vendor/ tree so
> implicitly versioned, so we also don't need anything from the go mod
> logic. We *maybe* should create the one-line go.mod that just declares
> the module, but even that I'm not sure of.

The goal of copying go.mod in from the Buildroot tree is to allow you to
override / provide go.mod if there is not one or if the existing one is
outdated with security issues, BEFORE the "go mod download" "go mod
vendor" steps performed in a download handler (the download step, before
the patch step).

> Can you explain why patch 2 is needed, converting runc's vendor.conf
> into a go.mod and go.sum?

Converting vendor.conf does network lookups with a non-deterministic
result.

> I may be blind, but I don't see this call to 'go mod download'... I suppose
> that it is in fact the 'go mod vendor' call that will implicitly do a download, no?

Yes, when GOPROXY=direct.

>  So, I think this should be chunked into:
>
> 1. Replace GOPATH with -mod=vendor - the same patch will need to update packages
> if needed to keep them building.

No updates to existing packages should be needed, and it's roughly the
same patch as what is in this RFC, except with the "copy in go.mod"
lines removed, and the "go mod vendor" lines removed.

> 2. Update packages to remove no-longer-needed _WORKSPACE etc. settings.
> 3. export GOPROXY=off

Already done in this patch, and would be done in step #1, not after step #2.

> 4. Run 'go mod vendor' in a post-extract hook. This should still be RFC because
> it violates the offline builds principle. Don't forget to mention some ideas of
> how the limitations could be overcome.

Not going to do this in a "real" solution.

> 5. Add a package (nsq?) that would be very difficult to support without the
> go.mod support.

I don't see why this is required for this, but that is the goal (to
allow external and internal packages that use go.mod and to transition
off of GOPATH for vendor/ based packages.)

> 6. Add support for Buildroot-supplied go.mod.

I agree this could be done after the initial minimal commit creating the
single-line go.mod file, setting GOPROXY=off, and setting -mod=vendor.

Best regards,
Christian

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 3/6] package/docker-containerd: upgrade to go modules
  2019-04-05  8:36   ` Arnout Vandecappelle
@ 2019-04-05 10:58     ` Christian Stewart
  0 siblings, 0 replies; 20+ messages in thread
From: Christian Stewart @ 2019-04-05 10:58 UTC (permalink / raw)
  To: buildroot

Hi Arnout,

Arnout Vandecappelle <arnout@mind.be> writes:
> On 17/03/2019 02:21, Christian Stewart wrote:
>>  DOCKER_CONTAINERD_LDFLAGS = \
>> -	-X github.com/docker/containerd.GitCommit=$(DOCKER_CONTAINERD_VERSION)
>> +	-X github.com/containerd/containerd.GitCommit=$(DOCKER_CONTAINERD_VERSION)
>
>  This looks like a completely independent change. Or is it because we now have a
> go.mod that calls the module containerd/containerd instead of docker/containerd?

This is a fix that could be applied seperately, IIF the root import path
is github.com/containerd/containerd in the current in-tree version (I'd
have to confirm to be sure).

>> +DOCKER_CONTAINERD_BUILD_TARGETS = \
>> +	github.com/containerd/containerd/cmd/ctr \
>> +	github.com/containerd/containerd/cmd/containerd \
>> +	github.com/containerd/containerd/cmd/containerd-shim
>
>  Same question, how is this related to the go modules?

Before, we specified which targets to build using a relative path like
./cmd/dockerd. Now, we can actually specify the fully qualified path to
the "main" package, and the Go tool is smart enough to locate the
correct path inside $(@D). It's also more readable to specify it this
way, and is considered best practice.

>  Would it be possible (and useful) to add those prefixes automatically?

I don't see why. Consider this: I can give you a list of packages:

 - k8s.io/cmd/apiserver
 - k8s.io/cmd/kubectl
 - k8s.io/cmd/kubeadm
 - github.com/containerd/containerd/cmd/ctr

... and all of these can be passed to "go build," which will resolve
them and produce the outputs with the correct binary names (the last
component of the path). It's not that much extra text to leave the
"github.com/containerd/containerd" part in. It's a point of personal
preference at this point though.

Best regards,
Christian

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-04-05 10:47       ` Christian Stewart
@ 2019-04-05 14:07         ` Arnout Vandecappelle
  2019-04-05 17:49           ` Christian Stewart
  0 siblings, 1 reply; 20+ messages in thread
From: Arnout Vandecappelle @ 2019-04-05 14:07 UTC (permalink / raw)
  To: buildroot



On 05/04/2019 12:47, Christian Stewart wrote:
> Hi Arnout,
> 
> 
> Arnout Vandecappelle <arnout@mind.be> writes:
>>>>> Cavets with the current implementation:
>>>>>
>>>>>  - "make source" may not produce a valid output
>>>>>  - module downloading occurs ignoring the Buildroot download server
>>>>>  - module downloading occurs in a post-patch hook
>>>> If it has all those caveats, so why do we want this ?
>>> Because these are issues with the initial WIP/RFC.
>>  Still, it would be good if you had at least some idea of how these could be solved.
> 
> Create a new package download method that uses the "go mod download" and
> "go mod vendor" steps seems to be the only way to go.

 +1


>>  So, here's a bite-sized piece: just convert GOPATH into GO111MODULE, but
>> assuming that the vendor/ tree is present. I believe that the current patch does
>> exactly that, but it *also* does something else...
>>>
>>> The most minimal version of this patch series would be to enable
>>> GO111MODULE, place a go.mod file which specifies the Go import path of
>>
>>  ... I understood from our previous conversation that the go.mod file is in fact
>> not needed. If the go.mod file is missing, then the go tool will just use the
>> vendor tree as is. Which is perfect for our current situation.
> 
> The file IS needed because the Go tool needs to know the root package
> path at $(@D). For example, in Docker, this is
> "github.com/docker/docker" even though the code comes from
> "github.com/docker/engine."

 Okay, I see... So e.g. in docker-containerd, if we would create a go.mod with
just "module github.com/docker/containerd" (note the 'incorrect' docker instead
of containerd), we would not have to change the -X ...GitCommit setting?


>>  So, IMO, the _APPLY_EXTRACT_GOMOD part should be removed from this patch. Then,
>> patch 1 becomes a really simple patch that people can get their head around. And
>> you get a much better baseline to explain what you want to do, since you no
>> longer have to compare against GOPATH; you only need to compare against a
>> non-instantiated vendor tree.
> 
> This part cannot be removed but the go.mod in the Buildroot tree and the
> module download / vendor steps could be for an initial version. I'll
> spin a series with this soon.

 I don't understand why it can't be removed. The APPLY_EXTRACT_GOMOD doesn't do
anything if a vendor tree is already present, and in all our current go packages
the vendor tree is already present...

 Oh, hang on, you probably still have to create an almost-empty go.mod for the
ones which don't have a vendor.conf.


>>  Actually, you would need to specify additional downloads, and a post-extract
>> hook to extract them into the vendor directory.
>>
>>  If you can give an example of a package that only has a few of these, I could
>> create an example of how it might work without any extra infra. Although I'm not
>> entirely sure if such an example would be very useful, except to show that it
>> would be extremely hard to maintain.
> 
> This doesn't make any sense to me.

 What doesn't make sense to you? Do you mean that you don't understand how it
could be done with additional downloads, or do you mean that you don't
understand how it could not be useful, or do you mean that you don't understand
how it could be hard to maintain, or do you mean that you fail to parse the
entire sentence?


>>  If that is the example, then it shows that it's definitely not the way we want
>> to go... But I don't think it's the example that you want to show, because
>> you're not yet using go modules to download stuff.
> 
> I am using Go modules to download stuff in this PoC patch.

 Can you point out which bit causes the modules to be downloaded?


>>> This is a Proof-of-Concept of how we might update a specific dependency
>>> with a critical CVE or other bug which is vendored into an old upstream
>>> vendor/ tree. The Buildroot maintainers, upon discovering the CVE or
>>> other bug, could update the specific dependency to a newer revision with
>>> the fix. This a "feature" and more speculative. It's not required for a
>>> v1 of this patch.
>>
>>  Is this something that we want to do? Go has apparently chosen for this
>> vendoring model where it is the responsibility of the package maintainers to
>> update their dependencies, and not of the distro. In that model, if the package
>> maintainers don't do their job, we're not going to fix the world for them. In
>> general, we only take fixes (even for CVEs) that come from upstream. Maybe we'll
>> backport a fix, but nothing more than that.
> 
> I don't see the point. If we discover a security issue, this provides a
> mechanism to override a dependency at download time.

 ... by keeping a copy of the entire go.mod and go.sum in Buildroot...

 But I see your point. It's the docker-cli thing that requires the entire
docker-specific patch for pflag that put me on the wrong foot.


> If this is not
> something that Buildroot wants I'm happy to just remove the 2 lines it
> takes to implement it in the code.
> 
>>  By using the vendoring approach, Go has basically chosen to make each CVE in a
>> library a CVE against all packages using that library, which has to be solved
>> separately in each user. Recursively.
> 
> They have not chosen to do this. The vendoring model is/was a stop-gap
> until something better (Modules) was developed.

 "The vendoring model" means that every package bundles its dependencies.
Whether those dependencies are maintained in the same git tree, or as git
submodules, or as go modules with an explicit version doesn't make much
difference. For the package developers it does make a world of difference, of
course. But for the distro maintainer it runs down to the same thing: updates of
dependencies are handled by the package developer, not by the distro maintainer.

 There are obviously advantages to the vendoring model. It relieves the package
developers from dealing with changing APIs of their dependencies, it allows them
to work around bugs in dependencies, and it makes sure that what they test is
also what the final user is going to get. Also for library developers it makes
things easier, because ABI compatibility is no longer a thing. And even for
distro maintainers it makes things easier, because in essence the distro no
longer needs to worry about dependencies. However, it has this one big
disadvantage that a vulnerability can no longer be fixed by just updating a
single package.


>>  nsq is a better example - it doesn't have a vendor tree nor submodules. And it
>> has an actual modern go.mod. And it's a nice example that trying to do this
>> manually within buildroot is not doable: the go.mod is huge. Like, npm-huge...
>>
>>  However, the current patch set does nothing to solve that, I think. So it would
>> be nice to have an example of how we might integrate something like nsq.
> 
> The current patch set DOES solve this. It would work fine AS IS, since
> the "go mod vendor" step downloads and extracts dependencies from go.mod.

 Yeah, I don't understand why I wrote that... The current patch solves exactly
that...


> The only awkward part of the RFC is that this behavior is not placed
> into a proper download mechanism rather than the extract step.

 Ack. A limitation which you clearly marked in your commit message.


>>>>> It also does not allow us the opportunity to validate or adjust
>>>>> dependency versions when upgrading packages in Buildroot.
>>>
>>> Please see my above comments re: updating a dependency version when a
>>> CVE or other bug is found.
>>
>>  As I explained above: Buildroot will not do that.
> 
> Why?

 My statement was a little too strong. Let's say that up to now we have not been
in the habit of doing that.


>>> Specifically, consider the case where we have
>>> an older LTS Buildroot branch pointing to an unmaintained older revision
>>> of the project. With vendor/ coming from the downloaded upstream source
>>> tarball, we would not be able to fix the CVE or bug without copying the
>>> patch in the Buildroot tree.
>>
>>  Well, actually we can... In any of the approaches we're considering, after the
>> extract step we will have all the dependencies present in the vendor/ tree. So
>> we can always add a patch for a CVE fix in a dependent package in the usual way
>> (we just have to add some directory components to the upstream patch).
> 
> I don't see why this is better than overriding go.mod?

 Because usually, changing the version brings in a whole lot more changes than
just the CVE fix.



>>  The only situation where this would not be the case is if we would let the go
>> tool do its downloads during the build step. But that is something we
>> *definitely* want to avoid.
> 
> This doesn't make any sense to me either.

 "this would not be the case" refers to your statement that we would not be able
to fix a CVE (by adding a .patch). My statement is: we are able to fix CVEs like
we do it for any other package, as long as go modules are instantiated/extracted
before the patch step.

>  Who proposed downloading
> during the build step?

 If we don't set GOPROXY=off, the Go tool might go off and download stuff.

> Why would this be the only case where adding a
> patch would not work? With a new download method, it would go:
> 
>  1. Download base package (containing go.mod)
>  2. (optional) Copy in go.mod and go.sum from Buildroot tree
>  3. Execute "go mod vendor." Note, go.sum ensures the result is consistent.
>  4. Compress the result and store in dl/ tree.
>  5. Later, in the extract step, extract this tarball
>  6. Apply any patches (this can also target the vendor/ tree if needed)
>  7. Build the package with -mod=vendor arg which uses the vendor/ tree.

 Exactly, so it is still possible to use the patch approach instead of changing
the version.

[snip]
>>  Easy: in the current sitatution, we *never* want the go tool to download
>> anything (because we expect everything to be present in the vendor/ tree), so we
>> can just globally export GOPROXY=off. IIUC.
> 
> This is already done in the above patch, with the exception of the "go
> mod vendor" step which does a download, which would be moved into a
> download provider in the actual change.
> 
>>  Note that this is something that would potentially break existing external
>> packages, so should be mentioned clearly in the release notes. It can be fixed
>> easily, of course, by setting GOPROXY=direct in <PKG>_GO_ENV.
> 
> How would this break external packages? The _GOMOD variable is
> automatically determined using the same logic that the GOPATH directory
> structure is determined today. This means that existing external
> packages using vendor/ would still build fine.

 An existing external package that doesn't use vendor/ but instead uses go.mod
(and thus downloads stuff in the build step, because the go tool will do that
automatically) would break. I don't know if this is even possible at the moment,
I'm just indicating a possible concern. Maybe I'm exaggerating and setting
GOPROXY=off doesn't really carry any risks.


>>  Given that this is a slightly more controversial thing, I would do it in a
>> separate patch from the GOPATH removal.
> 
> I'm not sure what specifically you're referring to here.>
>>  So,
>>
>> - if a go.mod is present, we can use the Go tool;
>> - if a legacy .toml or whatever is present, we can use the Go tool;
>> - if there is a vendor/ tree but no metadata, we need to create an almost-empty
>>   go.mod.
>>
>> Correct?
> 
> Yes, the go.mod needs to contain a single line to inform the Go tool of
> the package path of the root of the code.
> 
>>  And all of that is only needed to be able to do the 'go mod vendor' command in
>> a post-extract hook, right?
> 
> No, this is needed to get Go to use the vendor/ tree without a GOPATH,
> because it used to derive the root package path of the code from the
> location in the GOPATH tree.

 OK, got it!

> 
>>  What does 'go mod vendor' do if there is no go.mod or legacy file?
> 
> It fails.
> 
>>  What I also don't understand is why we would ever add a go.mod ourselves... If
>> upstream has one, clearly there is no reason to add one.
> 
> Upstream does not have one in a lot of cases (yet).
> 
>> If upstream has a .toml, then we can just use that. If upstream has
>> nothing, it means that everything is already in the vendor/ tree so
>> implicitly versioned, so we also don't need anything from the go mod
>> logic. We *maybe* should create the one-line go.mod that just declares
>> the module, but even that I'm not sure of.
> 
> The goal of copying go.mod in from the Buildroot tree is to allow you to
> override / provide go.mod if there is not one or if the existing one is
> outdated with security issues,

 There are two concerns here, and I think we should consider them separately.

1. Make sure that building with -mod works.
  a. Upstream has a go.mod -> nothing needs to be done.
  b. Upstream has no go.mod, but has vendor.conf or .toml -> nothing needs to be
done, the Go tool handles these as well.
  c. Upstream has no go.mod or vendor.conf or anything -> go.mod with just the
module line needs to be created.

2. Modify dependencies by changing their versions.
   -> Buildroot-supplied go.mod is required.

 So, if my above analysis is correct, a Buildroot-supplied go.mod is only needed
for the 'changing versions' scenario (which I am not convinced is something we
need). For the 'upstream has nothing' scenario, it is sufficient to generate the
one-line go.mod.

 Is that correct?


> BEFORE the "go mod download" "go mod
> vendor" steps performed in a download handler (the download step, before
> the patch step).
> 
>> Can you explain why patch 2 is needed, converting runc's vendor.conf
>> into a go.mod and go.sum?
> 
> Converting vendor.conf does network lookups with a non-deterministic
> result.

 Let me rephrase the question... As far as I understand, after the current patch
series, the Go tool will still not download anything for the existing packages,
because the vendor tree is already there. Is that correct? If not, could you
point out where the download happens?


>> I may be blind, but I don't see this call to 'go mod download'... I suppose
>> that it is in fact the 'go mod vendor' call that will implicitly do a download, no?
> 
> Yes, when GOPROXY=direct.
> 
>>  So, I think this should be chunked into:
>>
>> 1. Replace GOPATH with -mod=vendor - the same patch will need to update packages
>> if needed to keep them building.
> 
> No updates to existing packages should be needed, and it's roughly the
> same patch as what is in this RFC, except with the "copy in go.mod"
> lines removed, and the "go mod vendor" lines removed.

 Ah, of course, because the go.mod is autogenerated, it will still work. But
then why are those changes in docker-containerd.mk needed? I still don't get it :-(


>> 2. Update packages to remove no-longer-needed _WORKSPACE etc. settings.
>> 3. export GOPROXY=off
> 
> Already done in this patch, and would be done in step #1, not after step #2.

 I put this separate because AFAIU setting GOPROXY to off is not strictly
needed. In the current situation, the Go tool is never going to download
anything anyway, because the go.mod is empty.


>> 4. Run 'go mod vendor' in a post-extract hook. This should still be RFC because
>> it violates the offline builds principle. Don't forget to mention some ideas of
>> how the limitations could be overcome.
> 
> Not going to do this in a "real" solution.

 The real solution being the go download method, right?


>> 5. Add a package (nsq?) that would be very difficult to support without the
>> go.mod support.
> 
> I don't see why this is required for this, but that is the goal (to
> allow external and internal packages that use go.mod and to transition
> off of GOPATH for vendor/ based packages.)

 It's not *required*, it's just to show what is the use case for this work.


>> 6. Add support for Buildroot-supplied go.mod.
> 
> I agree this could be done after the initial minimal commit creating the
> single-line go.mod file, setting GOPROXY=off, and setting -mod=vendor.

 For this one, it would be nice if there could be an alternative that doesn't
require copying the entire go.mod and go.sum, but rather override only one or
two. Because I still think that that is going to be the only use case of this
feature.

 Regards,
 Arnout

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-04-05 14:07         ` Arnout Vandecappelle
@ 2019-04-05 17:49           ` Christian Stewart
  2019-04-05 21:59             ` Arnout Vandecappelle
  0 siblings, 1 reply; 20+ messages in thread
From: Christian Stewart @ 2019-04-05 17:49 UTC (permalink / raw)
  To: buildroot

Hi Arnout,

Arnout Vandecappelle <arnout@mind.be> writes:
>  Okay, I see... So e.g. in docker-containerd, if we would create a go.mod with
> just "module github.com/docker/containerd" (note the 'incorrect' docker instead
> of containerd), we would not have to change the -X ...GitCommit setting?

This would not work since the code expects a specific import path. Note
the difference between an "import path" and the actual path where the
code is hosted, and the actual path where the code is located on disk.
These are 3 different paths.

It's correct to update the setting to match the code.

>>>  So, IMO, the _APPLY_EXTRACT_GOMOD part should be removed from this patch. Then,
>>> patch 1 becomes a really simple patch that people can get their head around. And
>>> you get a much better baseline to explain what you want to do, since you no
>>> longer have to compare against GOPATH; you only need to compare against a
>>> non-instantiated vendor tree.
>> 
>> This part cannot be removed but the go.mod in the Buildroot tree and the
>> module download / vendor steps could be for an initial version. I'll
>> spin a series with this soon.
>
>  I don't understand why it can't be removed. The APPLY_EXTRACT_GOMOD doesn't do
> anything if a vendor tree is already present, and in all our current go packages
> the vendor tree is already present...

The hook can probably be removed but we need to make the almost-empty
go.mod somewhere. 

>  Oh, hang on, you probably still have to create an almost-empty go.mod for the
> ones which don't have a vendor.conf.

I'm almost sorry I mentioned vendor.conf or gopkg.toml in the first
place, becuase it seems like you're now distracted by the other formats.
It's not really a good idea to use the auto conversion at runtime since
the result is not as deterministic as having a predetetermined go.mod,
and the conversion requires network lookups.

Pretend the other formats don't exist. They are there for legacy only anyway.


>>>  If you can give an example of a package that only has a few of these, I could
>>> create an example of how it might work without any extra infra. Although I'm not
>>> entirely sure if such an example would be very useful, except to show that it
>>> would be extremely hard to maintain.
>> 
>> This doesn't make any sense to me.
>
>  What doesn't make sense to you? Do you mean that you don't understand how it
> could be done with additional downloads, or do you mean that you don't
> understand how it could not be useful, or do you mean that you don't understand
> how it could be hard to maintain, or do you mean that you fail to parse the
> entire sentence?

I suppose you're saying you want to add more download statements in the
package and then download all of the dependencies into the vendor/ tree
using the Buildroot mechanism. This is not viable and I don't see why we
would even consider it to begin with.

>  Can you point out which bit causes the modules to be downloaded?

GOPROXY=direct go mod vendor, or you might try executing the build with
the series applied, compiling any Go package, and you'll see the
download happen during that step.

>> I don't see the point. If we discover a security issue, this provides a
>> mechanism to override a dependency at download time.
>
>  ... by keeping a copy of the entire go.mod and go.sum in Buildroot...

So what? Is it really that big of a cost?

>  "The vendoring model" means that every package bundles its dependencies.
> Whether those dependencies are maintained in the same git tree, or as git
> submodules, or as go modules with an explicit version doesn't make much
> difference. For the package developers it does make a world of difference, of
> course. But for the distro maintainer it runs down to the same thing: updates of
> dependencies are handled by the package developer, not by the distro maintainer.

I suggest you research the original reason why vendor was added, GOPATH,
the old "go get" mechanism, and the general history of package
versioning in Go. I have not found any of these things to be the
original reasoning behind these features in Go, nor have I found it
referenced anywhere in their documentation. Vendor was added because the
old Go "go get" tool did not offer an opportunity to override broken
"master" branches of dependencies. When Go modules come around, it
allows us to forget about vendoring, since the module files specify
exact version hashes in the go.mod file, so you get an exact result.

I don't see any evidence that distro maintainers do not patch go.mod.

> There are obviously advantages to the vendoring model. It relieves the
> package developers from dealing with changing APIs of their
> dependencies, it allows them to work around bugs in dependencies, and
> it makes sure that what they test is also what the final user is going
> to get. Also for library developers it makes things easier, because
> ABI compatibility is no longer a thing. And even for distro
> maintainers it makes things easier, because in essence the distro no
> longer needs to worry about dependencies. However, it has this one big
> disadvantage that a vulnerability can no longer be fixed by just
> updating a single package.

I agree that it is a good idea to test the final result when you change
a dependency. But you can update a single package. You're
misunderstanding the behavior of the Go tool. If you update a single
line in go.mod, only a single package will be updated, since all of the
dependencies, indirect or not, are copied into go.mod to begin with.

>>>  Well, actually we can... In any of the approaches we're considering, after the
>>> extract step we will have all the dependencies present in the vendor/ tree. So
>>> we can always add a patch for a CVE fix in a dependent package in the usual way
>>> (we just have to add some directory components to the upstream patch).
>> 
>> I don't see why this is better than overriding go.mod?
>
>  Because usually, changing the version brings in a whole lot more changes than
> just the CVE fix.

I don't see why this is the case. Maybe in a versioning system like
package.json, but please see above on why Go modules are different.
Fully qualified version strings make this not the case.

>  "this would not be the case" refers to your statement that we would not be able
> to fix a CVE (by adding a .patch). My statement is: we are able to fix CVEs like
> we do it for any other package, as long as go modules are instantiated/extracted
> before the patch step.

Yeah, this is true, we can fix things this way. But usually, patches in
the buildroot tree are meant to be able to be applied with "git am"
against the original code repository, correct? This way that would not
be possible. Seems awkward.

>  If we don't set GOPROXY=off, the Go tool might go off and download stuff.

We set GOPROXY=off even today in master. The Go tool will NOT go off and
download stuff randomly :)

>> Why would this be the only case where adding a
>> patch would not work? With a new download method, it would go:
>> 
>>  1. Download base package (containing go.mod)
>>  2. (optional) Copy in go.mod and go.sum from Buildroot tree
>>  3. Execute "go mod vendor." Note, go.sum ensures the result is consistent.
>>  4. Compress the result and store in dl/ tree.
>>  5. Later, in the extract step, extract this tarball
>>  6. Apply any patches (this can also target the vendor/ tree if needed)
>>  7. Build the package with -mod=vendor arg which uses the vendor/ tree.
>
>  Exactly, so it is still possible to use the patch approach instead of changing
> the version.

... which was the point of me listing the order of things happening.
However, you can also see why it might be useful to have go.mod and
go.sum in the tree, considering that you are not patching BEFORE running
"go mod download" or "go mod vendor." You need an opportunity to
override those files earlier on, especially if a dependency no longer
exists, and you're looking to update the code URL for it.

Here's an example: sometimes, packages use "vanity urls" like
mydomain.com/mynamespace/mycode (or similar). The Go tool does send HTTP
requests to that domain, which usually will redirect the tool to a
Github repo.

It would be impossible with patches + the proposed ordering above to fix
the import URL if someone's domain were to go away randomly. The
Buildroot proxy helps here, of course.

>> How would this break external packages? The _GOMOD variable is
>> automatically determined using the same logic that the GOPATH directory
>> structure is determined today. This means that existing external
>> packages using vendor/ would still build fine.
>
>  An existing external package that doesn't use vendor/ but instead uses go.mod
> (and thus downloads stuff in the build step, because the go tool will do that
> automatically) would break. I don't know if this is even possible at the moment,
> I'm just indicating a possible concern. Maybe I'm exaggerating and setting
> GOPROXY=off doesn't really carry any risks.

None use this. This is not a backwards compatibility concern yet. I
understand your reasoning but GOPROXY=off in master so nobody should be
using this yet.

>> The goal of copying go.mod in from the Buildroot tree is to allow you to
>> override / provide go.mod if there is not one or if the existing one is
>> outdated with security issues,
>
>  There are two concerns here, and I think we should consider them separately.
>
> 1. Make sure that building with -mod works.
>   a. Upstream has a go.mod -> nothing needs to be done.

This is the currently implemented logic in the PoC patches.

>   b. Upstream has no go.mod, but has vendor.conf or .toml -> nothing needs to be
> done, the Go tool handles these as well.

I don't recommend this, since the conversion is not deterministic and
does network calls, as I mentioned above.

Copying in the go.mod and go.sum might have spooked you because of the
line count and the hashes in the file. But the reason for it is really
really intentional. I don't want to introduce points of uncertainty into
the build process.

Isn't this a bigger priority for us than a lower line count in the repo??

For this reason I regret mentioning vendor.conf conversion at all.

>   c. Upstream has no go.mod or vendor.conf or anything -> go.mod with just the
> module line needs to be created.

Yup, currently implemented.

> 2. Modify dependencies by changing their versions.
>    -> Buildroot-supplied go.mod is required.

Sort-of, unless we can make a way to patch it before we run the download process.

>  So, if my above analysis is correct, a Buildroot-supplied go.mod is only needed
> for the 'changing versions' scenario (which I am not convinced is something we
> need). For the 'upstream has nothing' scenario, it is sufficient to generate the
> one-line go.mod.
>
>  Is that correct?

Sure, and I'm happy to NOT allow package maintainers to override it by
placing a go.mod and go.sum next to their .mk file. I don't know why
limiting this is an advantage. You could just say that developers can
place those files if they want in their external packages, and never do
it in the Buildroot tree. But why close off the functionality? I'm going
to have to keep a custom patch on top of Buildroot to add it back in
again for my own projects and external packages otherwise.

Maybe you think you need to manually maintain the go.mod and go.sum
files. This isn't the case. We would use the Go tool to convert the
vendor.conf or whatever from upstream, check the result for consistency,
then update the go.mod and go.sum in the Buildroot tree with the output
from this. But, as far as I can see, none of the packages in the
mainline Buildroot tree will need this (yet). But you may as well leave
the 2 lines in the code to enable external packages to make this choice.

>>> Can you explain why patch 2 is needed, converting runc's vendor.conf
>>> into a go.mod and go.sum?
>> 
>> Converting vendor.conf does network lookups with a non-deterministic
>> result.

This is an exact example of what I'm describing in the previous paragraph.

>  Let me rephrase the question... As far as I understand, after the current patch
> series, the Go tool will still not download anything for the existing packages,
> because the vendor tree is already there. Is that correct? If not, could you
> point out where the download happens?

"go mod vendor"

Although, if you look at the code, if the vendor tree is present, this
is not called.

When compiling, go build -mod=vendor will tell the tool to use vendor/
and use the go.mod file only for the module path line (roughly
equiv to just the 1-line go.mod file).

>  Ah, of course, because the go.mod is autogenerated, it will still work. But
> then why are those changes in docker-containerd.mk needed? I still don't get it :-(

This series could be done without those, using vendor in the tree. It's
an example.

> I put this separate because AFAIU setting GOPROXY to off is not
> strictly needed. In the current situation, the Go tool is never going
> to download anything anyway, because the go.mod is empty.

It's strictly needed. The Go tool will analyze the code and determine
the import paths of all the code files, and then generate / update
go.mod. You might want to run the patch series and observe the behavior yourself.

>>> 4. Run 'go mod vendor' in a post-extract hook. This should still be RFC because
>>> it violates the offline builds principle. Don't forget to mention some ideas of
>>> how the limitations could be overcome.
>> 
>> Not going to do this in a "real" solution.
>
>  The real solution being the go download method, right?

Most likely, but I also would just rule out doing any downloading
outside of the download step. This makes sense, right?

>>> 5. Add a package (nsq?) that would be very difficult to support without the
>>> go.mod support.
>> 
>> I don't see why this is required for this, but that is the goal (to
>> allow external and internal packages that use go.mod and to transition
>> off of GOPATH for vendor/ based packages.)
>
>  It's not *required*, it's just to show what is the use case for this work.

Hey, I'm all for adding new packages, required or not.

>>> 6. Add support for Buildroot-supplied go.mod.
>> 
>> I agree this could be done after the initial minimal commit creating the
>> single-line go.mod file, setting GOPROXY=off, and setting -mod=vendor.
>
>  For this one, it would be nice if there could be an alternative that doesn't
> require copying the entire go.mod and go.sum, but rather override only one or
> two. Because I still think that that is going to be the only use case of this
> feature.

Maybe, but you're making a lot of absolute statements here without
actually using the Go modules workflow yourself to know why those
statements are or aren't true. Please have a look at my reasoning above
and see what you think.

Best regards,
Christian 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-04-05 17:49           ` Christian Stewart
@ 2019-04-05 21:59             ` Arnout Vandecappelle
  2019-04-06  3:13               ` Christian Stewart
  0 siblings, 1 reply; 20+ messages in thread
From: Arnout Vandecappelle @ 2019-04-05 21:59 UTC (permalink / raw)
  To: buildroot



On 05/04/2019 19:49, Christian Stewart wrote:
> Hi Arnout,
> 
> Arnout Vandecappelle <arnout@mind.be> writes:
[snip]
>>  I don't understand why it can't be removed. The APPLY_EXTRACT_GOMOD doesn't do
>> anything if a vendor tree is already present, and in all our current go packages
>> the vendor tree is already present...
> 
> The hook can probably be removed but we need to make the almost-empty
> go.mod somewhere. 
> 
>>  Oh, hang on, you probably still have to create an almost-empty go.mod for the
>> ones which don't have a vendor.conf.

 My bad, I forgot that that was also done in the post-extract hook.


> I'm almost sorry I mentioned vendor.conf or gopkg.toml in the first
> place, becuase it seems like you're now distracted by the other formats.

 :-)


> It's not really a good idea to use the auto conversion at runtime since
> the result is not as deterministic as having a predetetermined go.mod,
> and the conversion requires network lookups.

 AFAIU, packages which use vendor.conf or gopkg.toml will have their vendor
trees bundled, no? Otherwise other users wouldn't have a reliable way of
downloading the package anyway, right?


> Pretend the other formats don't exist. They are there for legacy only anyway.

 Okay. Anyway, the go.mod will be autogenerated since it will be missing, and
supposedly the vendor tree is there.


>>>>  If you can give an example of a package that only has a few of these, I could
>>>> create an example of how it might work without any extra infra. Although I'm not
>>>> entirely sure if such an example would be very useful, except to show that it
>>>> would be extremely hard to maintain.
>>>
>>> This doesn't make any sense to me.
>>
>>  What doesn't make sense to you? Do you mean that you don't understand how it
>> could be done with additional downloads, or do you mean that you don't
>> understand how it could not be useful, or do you mean that you don't understand
>> how it could be hard to maintain, or do you mean that you fail to parse the
>> entire sentence?
> 
> I suppose you're saying you want to add more download statements in the
> package and then download all of the dependencies into the vendor/ tree
> using the Buildroot mechanism.

 Not really download statements; _EXTRA_DOWNLOADS, and a post-extract hook to
extract them. In terms of complexity, it's pretty similar as carrying the go.mod
in Buildroot. But of course, this was would not be able to use an existing
upstream go.mod.

> This is not viable 

 Why is it not viable?

> and I don't see why we
> would even consider it to begin with.

 Because we have the option of either using the existing mechanisms, or to
extend the infra to cover go modules. To evaluate whether the extra infra is
useful, it is interesting to look at what it would look at what would be needed
without it.


>>  Can you point out which bit causes the modules to be downloaded?
> 
> GOPROXY=direct go mod vendor, or you might try executing the build with
> the series applied, compiling any Go package, and you'll see the
> download happen during that step.

 I'll admit that I hadn't done that yet. So I applied only the first patch and
tried to build docker-cli. It doesn't do any downloading of dependencies because
the vendor/ tree is already there (the patch has an explicit condition for that).

 BTW, the build step then does automatic conversion of vendor.conf, but the
build fails with:

can't load package: package cmd/docker: cannot find package "." in:
        /home/arnout/src/buildroot/output/host/lib/go/src/cmd/docker

 I'm probably doing something wrong :-)

[snip]
>>> Why would this be the only case where adding a
>>> patch would not work? With a new download method, it would go:
>>>
>>>  1. Download base package (containing go.mod)
>>>  2. (optional) Copy in go.mod and go.sum from Buildroot tree
>>>  3. Execute "go mod vendor." Note, go.sum ensures the result is consistent.
>>>  4. Compress the result and store in dl/ tree.

 I just noticed something missing: you'll need to add host-go to the
DOWNLOAD_DEPENDENCIES of go packages if you want to use the go tool in the
download step.

>>>  5. Later, in the extract step, extract this tarball
>>>  6. Apply any patches (this can also target the vendor/ tree if needed)
>>>  7. Build the package with -mod=vendor arg which uses the vendor/ tree.
>>
>>  Exactly, so it is still possible to use the patch approach instead of changing
>> the version.
> 
> ... which was the point of me listing the order of things happening.
> However, you can also see why it might be useful to have go.mod and
> go.sum in the tree, considering that you are not patching BEFORE running
> "go mod download" or "go mod vendor." You need an opportunity to
> override those files earlier on, especially if a dependency no longer
> exists, and you're looking to update the code URL for it.

 Agreed.

 I'm not communicating my message very well apparently. I am not saying that a
Buildroot-supplied go.mod is not useful. I'm only saying that it is not
absolutely needed, and that the current infra probably already covers a lot if
not all of the use cases we have in reality. While explaining why I think that,
I apparently didn't make it clear that I'm only trying to compare the different
options we have, and weigh that against the priorities of the project.

[snip]
>>> The goal of copying go.mod in from the Buildroot tree is to allow you to
>>> override / provide go.mod if there is not one or if the existing one is
>>> outdated with security issues,
>>
>>  There are two concerns here, and I think we should consider them separately.
>>
>> 1. Make sure that building with -mod works.
>>   a. Upstream has a go.mod -> nothing needs to be done.
> 
> This is the currently implemented logic in the PoC patches.
> 
>>   b. Upstream has no go.mod, but has vendor.conf or .toml -> nothing needs to be
>> done, the Go tool handles these as well.

 I think my analysis was wrong. It should have been:

a. Upstream has a go.mod.
 -> Dependencies have to be downloaded. Handled by the current patch, with the 3
limitations mentioned.

b. Upstream has a bundled vendor/ tree (as part of the tarball or as git
submodules).
 -> go.mod has to be created with just the module name. Handled by the current
patch, no limitations.

c. Upstream doesn't have a bundled vendor/ tree, only vendor.conf or gopkg.toml.
 -> not reproducible because the vendor.conf and gopkg.toml don't encode the
version to download. (Note: I looked at the vendor.conf of docker-cli and it
does have a version, so I'm not sure if this is true).

 For the c case, I indeed see no other way than a Buildroot-provided go.mod that
I can think of.


> I don't recommend this, since the conversion is not deterministic and

 Could you maybe explain why it is not deterministic?

> does network calls, as I mentioned above.

 The network calls are the same as in case a I think, no?


> Copying in the go.mod and go.sum might have spooked you because of the
> line count and the hashes in the file. 

 Yes indeed :-)

 Note that I don't think we need go.sum. We do with the current patch, but if
the go download becomes a download method, we end up with a tarball that
contains all dependencies, and the buildroot hashing is done on that tarball. So
checksumming is done (though it's a little late, so finding out what went wrong
in case of hash mismatch might be tricky).

[snip]
>>  Let me rephrase the question... As far as I understand, after the current patch
>> series, the Go tool will still not download anything for the existing packages,
>> because the vendor tree is already there. Is that correct? If not, could you
>> point out where the download happens?
> 
> "go mod vendor"
> 
> Although, if you look at the code, if the vendor tree is present, this
> is not called.

 That was my point: all current packages have a vendor tree, so it never gets
called.


> When compiling, go build -mod=vendor will tell the tool to use vendor/
> and use the go.mod file only for the module path line (roughly
> equiv to just the 1-line go.mod file).
> 
>>  Ah, of course, because the go.mod is autogenerated, it will still work. But
>> then why are those changes in docker-containerd.mk needed? I still don't get it :-(
> 
> This series could be done without those, using vendor in the tree. It's
> an example.
> 
>> I put this separate because AFAIU setting GOPROXY to off is not
>> strictly needed. In the current situation, the Go tool is never going
>> to download anything anyway, because the go.mod is empty.
> 
> It's strictly needed. The Go tool will analyze the code and determine
> the import paths of all the code files, and then generate / update
> go.mod. You might want to run the patch series and observe the behavior yourself.

 The behaviour I observe (with only patch 1 and removing GOPROXY=off) is that
there is a single-line go.mod (generated by Buildroot) and no go.sum, and no
downloads are done. Which makes sense, because the vendor tree is already there.


 Note that I'm not saying that GOPROXY should not be set to off. What I'm saying
is that that is pretty much independent of the rest of the go module support.

[snip]
>>  For this one, it would be nice if there could be an alternative that doesn't
>> require copying the entire go.mod and go.sum, but rather override only one or
>> two. Because I still think that that is going to be the only use case of this
>> feature.
> 
> Maybe, but you're making a lot of absolute statements here without
> actually using the Go modules workflow yourself to know why those
> statements are or aren't true.

 I don't intend to make absolute statements, but I can hardly write AFAIU on
every line :-)

 Regards,
 Arnout


> Please have a look at my reasoning above
> and see what you think.
> 
> Best regards,
> Christian 
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-04-05 21:59             ` Arnout Vandecappelle
@ 2019-04-06  3:13               ` Christian Stewart
  0 siblings, 0 replies; 20+ messages in thread
From: Christian Stewart @ 2019-04-06  3:13 UTC (permalink / raw)
  To: buildroot

Hi Arnout,

Arnout Vandecappelle <arnout@mind.be> writes:
>> It's not really a good idea to use the auto conversion at runtime since
>> the result is not as deterministic as having a predetetermined go.mod,
>> and the conversion requires network lookups.
>
>  AFAIU, packages which use vendor.conf or gopkg.toml will have their vendor
> trees bundled, no? Otherwise other users wouldn't have a reliable way of
> downloading the package anyway, right?

No. Not necessarily. Especially in the case of Gopkg.toml.

However, in any case where it is bundled, we can just use it - and this
is the current behavior of this PoC series.

>> Pretend the other formats don't exist. They are there for legacy only anyway.
>
> Okay. Anyway, the go.mod will be autogenerated since it will be
> missing, and supposedly the vendor tree is there.

No, the tool will not know what the path to the root module is.

>> I suppose you're saying you want to add more download statements in the
>> package and then download all of the dependencies into the vendor/ tree
>> using the Buildroot mechanism.
>
>  Not really download statements; _EXTRA_DOWNLOADS, and a post-extract hook to
> extract them. In terms of complexity, it's pretty similar as carrying the go.mod
> in Buildroot. But of course, this was would not be able to use an existing
> upstream go.mod.

It's not similar at all to the complexity of carrying go.mod in
Buildroot. The Go tool manages an immensely complex process of resolving
the import paths to download URLs, fetching the sparse commits from the
source repositories, hashing the code for consistency, and linking it
all together so that the imports resolve correctly.

This is particularly complex with aliased imports in go.mod, which
Kubernetes uses for example.

Carrying the go.mod and go.sum allows the Go tool to manage all of this
for us while still keeping the code source hashes in a file similar to
my-package.hash.

>  Why is it not viable?

The maintenance process for carrying go.mod and go.sum is as follows:

 1. Clone the project and checkout the correct revision in GOPATH.
 2. Run "go mod init"
 3. Copy go.mod and go.sum to Buildroot.

That's it.

The maintenence process for what you're describing is, I'd imagine:

 1. Clone the project and checkout the correct revision
 2. Read through the dependencies and determine the versions of all.
 3. Write the download paths for the code into Buildroot BY HAND.
 4. Download all of the sources and hash them BY HAND.

Nobody is going to do this.

Perhaps the misunderstanding here is in how the go.mod and go.sum files
are managed. It's almost completely autonomous by the Go tool. We could
even write a command in Buildroot that generates them for us. If you're
imagining that I wrote all of these for docker-cli by hand, no wonder
you're alarmed at the prospect of including them in the Buildroot tree.

>  Because we have the option of either using the existing mechanisms, or to
> extend the infra to cover go modules. To evaluate whether the extra infra is
> useful, it is interesting to look at what it would look at what would be needed
> without it.

That's valid, and I believe it's an intractable approach, due to the
immense development overhead as described above.

>> GOPROXY=direct go mod vendor, or you might try executing the build with
>> the series applied, compiling any Go package, and you'll see the
>> download happen during that step.
>
>  I'll admit that I hadn't done that yet. So I applied only the first patch and
> tried to build docker-cli. It doesn't do any downloading of dependencies because
> the vendor/ tree is already there (the patch has an explicit condition for that).
>
>  BTW, the build step then does automatic conversion of vendor.conf, but the
> build fails with:
>
> can't load package: package cmd/docker: cannot find package "." in:
>         /home/arnout/src/buildroot/output/host/lib/go/src/cmd/docker
>
>  I'm probably doing something wrong :-)

Docker CLI requires a patch because they use some non-standard logic for
wiring together some adjusted vendored dependencies.

>  I just noticed something missing: you'll need to add host-go to the
> DOWNLOAD_DEPENDENCIES of go packages if you want to use the go tool in the
> download step.

Noted, thanks.

>  Agreed.
>
>  I'm not communicating my message very well apparently. I am not saying that a
> Buildroot-supplied go.mod is not useful. I'm only saying that it is not
> absolutely needed, and that the current infra probably already covers a lot if
> not all of the use cases we have in reality. While explaining why I think that,
> I apparently didn't make it clear that I'm only trying to compare the different
> options we have, and weigh that against the priorities of the project.

It is necessary in some cases when the package is using some "hack"
scripts in their tree to wire together dependencies at build time in a
non-standard way. The go.mod format allows us to codify these
adjustments in a way that the Go tool understands natively.

See docker-cli. I included these patches not to alarm you guys with
superfulous hash files, but to show examples of where these adjustments
allow us to fix quirks and simplify package build processes as well as
improve build consistency and reproducability.

>>>   b. Upstream has no go.mod, but has vendor.conf or .toml -> nothing needs to be
>>> done, the Go tool handles these as well.
>
>  I think my analysis was wrong. It should have been:
>
> a. Upstream has a go.mod.
>  -> Dependencies have to be downloaded. Handled by the current patch, with the 3
> limitations mentioned.

... unless they include vendor/ in the tree with go.mod, which sometimes
happens, and is indeed already handled by the PoC patch series (which
should be able to handle ANY condition as written with the quirks I
listed before).

> b. Upstream has a bundled vendor/ tree (as part of the tarball or as git
> submodules).
>  -> go.mod has to be created with just the module name. Handled by the current
> patch, no limitations.

Yes.

> c. Upstream doesn't have a bundled vendor/ tree, only vendor.conf or gopkg.toml.
>  -> not reproducible because the vendor.conf and gopkg.toml don't encode the
> version to download. (Note: I looked at the vendor.conf of docker-cli and it
> does have a version, so I'm not sure if this is true).
>
>  For the c case, I indeed see no other way than a Buildroot-provided go.mod that
> I can think of.
>
> Could you maybe explain why it is not deterministic?

They encode versions, but unfortunately they don't encode the transient
dependency versions (in all cases).

>  The network calls are the same as in case a I think, no?

No, go.mod and go.sum encode all the dependency versions, and also store
hashes of the code as well, so it's guaranteed to produce an identical result.

> Note that I don't think we need go.sum. We do with the current patch,
> but if the go download becomes a download method, we end up with a
> tarball that contains all dependencies, and the buildroot hashing is
> done on that tarball. So checksumming is done (though it's a little
> late, so finding out what went wrong in case of hash mismatch might be
> tricky).

Maybe so, but the Go tool would be quite happy to do the summing for us.

Please note that this file is NOT hand written.

> That was my point: all current packages have a vendor tree, so it never gets
> called.

Because it is not currently possible to add a package without a vendor tree.

The fact that all packages work one way today doesn't factor into the
optimal solution, at least in my mind, aside from backwards
compatiblity, which we do have here.

Note Docker required specific touchups with the GOPATH approach before.

The reason why the build failed when you tried it with just the first
patch, is because previously the docker-cli package overrided the
WORKSPACE variables to set the Go package path. The Go package path is
automatically inferred to be "github.com/docker/cli" instead of the
correct "github.com/docker/docker" with just the first patch and not the
docker-cli touchup patch.

>> [GOPROXY=off] is strictly needed. The Go tool will analyze the code
>> and determine the import paths of all the code files, and then
>> generate / update go.mod. You might want to run the patch series and
>> observe the behavior yourself.
>
> The behaviour I observe (with only patch 1 and removing GOPROXY=off)
> is that there is a single-line go.mod (generated by Buildroot) and no
> go.sum, and no downloads are done. Which makes sense, because the
> vendor tree is already there.

Yes, this is the correct behavior when vendor/ exists, and there is no
go.mod and go.sum in the Buildroot tree to override it. If there is a
go.mod and go.sum in the Buildroot tree, the PoC patch deletes vendor/
and replaces it via "go mod vendor."

>  Note that I'm not saying that GOPROXY should not be set to off. What
> I'm saying is that that is pretty much independent of the rest of the
> go module support.

If you just want Go module support without go.mod and go.sum and any
network fetching, then you have to change the following two environment
variables:

 - GO111MODULE: off -> on
 - GOPROXY: [none] (defaults to "direct") -> "off"

When we turn GO111MODULE to on, we imply GOPROXY=direct. This will cause
network calls during the "build" call.

>> Maybe, but you're making a lot of absolute statements here without
>> actually using the Go modules workflow yourself to know why those
>> statements are or aren't true.
>
>  I don't intend to make absolute statements, but I can hardly write AFAIU on
> every line :-)

I've provided a lot of examples of where overriding go.mod is useful:

 - When we add a download provider, we need an opportunity to adjust
   go.mod before the download step, which is far before the PATCH step.
 - Older Buildroot versions will use older releases of packages.
   Developers do not typically update the vendor/ tree of old releases.
 - Some packages use weird monkey-patching of GOPATH in their build
   scripts, which can be replicated with a single go.mod file instead.

I feel like it's not that big of a deal to copy go.mod and go.sum from
the package directory in Buildroot if it exists, and then agree to not
commit these files to the mainline Buildroot tree. The "copy them in"
step would be used by external packages, then. Even then, it's not that
big of a deal, I'm happy to remove the "copy these files in" line, so
this feels a bit like a garden shed :)

Best,
Christian

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-03-17  1:21 [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Christian Stewart
                   ` (5 preceding siblings ...)
  2019-03-27 16:50 ` [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Thomas Petazzoni
@ 2019-04-07 20:17 ` Thomas Petazzoni
  2019-04-08  2:04   ` Christian Stewart
  6 siblings, 1 reply; 20+ messages in thread
From: Thomas Petazzoni @ 2019-04-07 20:17 UTC (permalink / raw)
  To: buildroot

Hello Christian,

On Sat, 16 Mar 2019 18:21:37 -0700
Christian Stewart <christian@paral.in> wrote:

> This commit moves from the GOPATH mechanism to the new GO111MODULE approach for
> Go based packages. Old Go packages (with the exception of docker-cli) will
> compile without changes (for example, mender, flanneld).

While browsing through the list of pending patches, I found the
following patch series related to Go and package management:

  http://patchwork.ozlabs.org/project/buildroot/list/?series=68102

The first patch in the series,
http://patchwork.ozlabs.org/patch/976322/, introduces:

+	  Glide is a tool for managing the _vendor_ directory within a Go
+	  package. This feature, first introduced in Go 1.5, allows each
+	  package to have a _vendor_ directory containing dependent
+	  packages for the project. These vendor packages can be
+	  installed by a tool (e.g. glide), similar to go get or they
+	  can be vendored and distributed with the package.

and it does a fair amount of changes in pkg-golang.mk.

How does this fit (or not) with the changes you're proposing ?

Thanks,

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-04-07 20:17 ` Thomas Petazzoni
@ 2019-04-08  2:04   ` Christian Stewart
  2019-04-08  7:02     ` Thomas Petazzoni
  0 siblings, 1 reply; 20+ messages in thread
From: Christian Stewart @ 2019-04-08  2:04 UTC (permalink / raw)
  To: buildroot

Hi Thomas,

Thomas Petazzoni <thomas.petazzoni@bootlin.com> writes:
> While browsing through the list of pending patches, I found the
> following patch series related to Go and package management:
>
>   http://patchwork.ozlabs.org/project/buildroot/list/?series=68102
>
> The first patch in the series,
> http://patchwork.ozlabs.org/patch/976322/, introduces:
>
> +	  Glide is a tool for managing the _vendor_ directory within a Go
> +	  package. This feature, first introduced in Go 1.5, allows each
> +	  package to have a _vendor_ directory containing dependent
> +	  packages for the project. These vendor packages can be
> +	  installed by a tool (e.g. glide), similar to go get or they
> +	  can be vendored and distributed with the package.
>
> and it does a fair amount of changes in pkg-golang.mk.
>
> How does this fit (or not) with the changes you're proposing ?

Glide and other tools were built before Go modules were built. I do not
recommend integrating them into Buildroot. They are on the way out.

Please see the Go module wiki:

Specifically:

  go mod init automatically translates the required information from
  dep, glide, govendor, godep and 5 other pre-existing dependency
  managers into a go.mod file that produces the equivalent build.

The Go developers left it up to the community to develop a source
package manager, which is why Glide, Dep (gopkg.toml), vendor.conf, and
other formats exist. These formats are intended to be completely
replaced by go.mod.

My suggestion is to instead solve the Go module stuff, and then bring in
Glide-based packages with the glide.lock file converted into the Go
module format via the above automatic process.

Best regards,
Christian

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-04-08  2:04   ` Christian Stewart
@ 2019-04-08  7:02     ` Thomas Petazzoni
  2019-04-08  7:06       ` Christian Stewart
  0 siblings, 1 reply; 20+ messages in thread
From: Thomas Petazzoni @ 2019-04-08  7:02 UTC (permalink / raw)
  To: buildroot

Hello Christian,

Thanks a lot for your feedback!

On Sun, 07 Apr 2019 19:04:00 -0700
Christian Stewart <christian@paral.in> wrote:

> My suggestion is to instead solve the Go module stuff, and then bring in
> Glide-based packages with the glide.lock file converted into the Go
> module format via the above automatic process.

OK, thanks! What is the specific situation of the three Go packages
that Fabio wanted to integrate, namely bitcoin, btcd and neutrino ?
Will they be readily usable with the Go module integration ?

Best regards,

Thomas
-- 
Thomas Petazzoni, CTO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration
  2019-04-08  7:02     ` Thomas Petazzoni
@ 2019-04-08  7:06       ` Christian Stewart
  0 siblings, 0 replies; 20+ messages in thread
From: Christian Stewart @ 2019-04-08  7:06 UTC (permalink / raw)
  To: buildroot

Hi Thomas,


Thomas Petazzoni <thomas.petazzoni@bootlin.com> writes:
> OK, thanks! What is the specific situation of the three Go packages
> that Fabio wanted to integrate, namely bitcoin, btcd and neutrino ?
> Will they be readily usable with the Go module integration ?

Both of these projects already seem to be using go.mod:

 - https://github.com/btcsuite/btcd
 - https://github.com/lightninglabs/neutrino

... I can submit PoC patches implementing them trivially with the go
module PoC patch in place.

Best regards,
Christian

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2019-04-08  7:06 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-17  1:21 [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Christian Stewart
2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 2/6] package/runc: upgrade to go modules Christian Stewart
2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 3/6] package/docker-containerd: " Christian Stewart
2019-04-05  8:36   ` Arnout Vandecappelle
2019-04-05 10:58     ` Christian Stewart
2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 4/6] docker-cli: " Christian Stewart
2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 5/6] docker-proxy: " Christian Stewart
2019-03-17  1:21 ` [Buildroot] [RFC PATCH v1 6/6] package/docker-engine: " Christian Stewart
2019-03-27 16:50 ` [Buildroot] [RFC PATCH v1 1/6] package/go: implement go modules integration Thomas Petazzoni
2019-03-27 18:36   ` Christian Stewart
2019-04-05  8:32     ` Arnout Vandecappelle
2019-04-05 10:47       ` Christian Stewart
2019-04-05 14:07         ` Arnout Vandecappelle
2019-04-05 17:49           ` Christian Stewart
2019-04-05 21:59             ` Arnout Vandecappelle
2019-04-06  3:13               ` Christian Stewart
2019-04-07 20:17 ` Thomas Petazzoni
2019-04-08  2:04   ` Christian Stewart
2019-04-08  7:02     ` Thomas Petazzoni
2019-04-08  7:06       ` Christian Stewart

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.