├── .github ├── scripts │ ├── ci-after.sh │ └── ci-before.sh └── workflows │ └── sync.yml ├── .gitignore ├── Dockerfile ├── Dockerfile.local ├── LICENSE ├── README.md ├── build ├── build.sh └── lib │ └── var.sh ├── cmd ├── check.go ├── root.go ├── sum.go └── sync.go ├── core ├── checksum.go ├── gcr.go ├── manifests.go ├── option.go ├── sync.go └── types.go ├── go.mod ├── go.sum └── main.go /.github/scripts/ci-after.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | docker login -u zhangguanzhang -p ${DOCKER_PASS} 4 | 5 | cd $HOME 6 | mkdir -p temp 7 | cd temp 8 | 9 | cp $HOME/sync/bolt.db . 10 | ls -lh 11 | 12 | cat>Dockerfile< `registry.aliyuncs.com/k8sxio`,目前`gcr.io/google_containers`已经不和`k8s.gcr.io`一致了,所以目前只同步`k8s.gcr.io` 8 | 9 | 目前已经同步完了,可以看[github action的运行状态](https://github.com/zhangguanzhang/google_containers/actions) 10 | 11 | 12 | 查看镜像列表的话去[阿里云镜像仓库市场](https://cr.console.aliyun.com/cn-hangzhou/instances/images) 13 | 14 | 登录后在搜索框那输入`k8sxio/`搜索即可,也可以看单独的镜像例如`k8sxio/kube-apiserver` 15 | 16 | ## 特性 17 | 18 | - **不依赖 Docker 运行** 19 | - **同步期间不占用本地磁盘空间(直接通过标准库转发镜像)** 20 | - **可控的并发同步(优雅关闭/可调节并发数量)** 21 | 22 | 23 | - 核心拷贝方法引用的[containers/image](https://github.com/containers/image),部分代码借鉴了[mritd](https://github.com/mritd/imgsync) 24 | 25 | - 利用 boltdb 存储每个镜像 manifest 信息的 crc32 校验值,通过比对判断是否需要同步,而不是每次请求目标仓库 26 | - 把 boltdb 文件放 docker镜像里存在 dockerhub 上,多次运行 action 来同步 27 | 28 | 29 | ## 用法 30 | 31 | 编译的话记得带上tag,关闭CGO 32 | ```shell 33 | go build -tags=containers_image_openpgp main.go 34 | ``` 35 | 36 | sync命令是同步的命令,sum是查看boltdb文件的信息 37 | ```cassandraql 38 | imgsync sync --help 39 | 40 | Sync docker images. 41 | 42 | Usage: 43 | imgsync sync [flags] 44 | 45 | Flags: 46 | --addition-ns stringArray addition ns to sync (default []) 47 | --command-timeout duration timeout for the command execution. 48 | --db string the boltdb file (default "bolt.db") 49 | -h, --help help for sync 50 | --img-timeout duration sync single image timeout. (default 15m0s) 51 | --live-interval duration live output for travis-ci. 52 | --login-retry uint8 login retry when timeout. (default 2) 53 | -p, --password string The password to push. 54 | --process-limit int sync process limit. (default 2) 55 | --push-ns string the ns push to 56 | --push-to string the repo push to (default "docker.io") 57 | --query-limit int http query limit. (default 10) 58 | --retry int retry count while err. (default 4) 59 | --retry-interval duration retry interval while err. (default 4s) 60 | -u, --user string The username to push. 61 | 62 | Global Flags: 63 | --debug debug mode 64 | 65 | ``` 66 | 67 | ### 示例 68 | 69 | 那里设置好环境变量来控制运行的一些属性,也可以把镜像同步到自己的内网仓库上 70 | ```cassandraql 71 | ${HOME}/sync/imgsync sync 72 | --db ${HOME}/sync/bolt.db 73 | --push-to registry.aliyuncs.com 74 | --password ${PASS} 75 | --push-ns=k8sxio 76 | --user zhangguanzhang@qq.com 77 | --command-timeout ${TMOUT} 78 | --process-limit ${PROCESS:=2} 79 | --img-timeout ${IMG_TMOUT:=10m} 80 | --live-interval ${LIVE:=9m20s} 81 | --login-retry ${LOGIN_RETRY:=2} 82 | --debug=${DEBUG:=false} 83 | ``` -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$DEBUG" ] && set -x 4 | 5 | #脚本要存放在项目根目录 6 | readonly PRO_ROOT=$(cd $(dirname ${BASH_SOURCE:-$0})/../; pwd -P) 7 | source "${PRO_ROOT}/build/lib/var.sh" 8 | 9 | read TAG_NUM LDFLAGS < <(GONELIST::SetVersion) 10 | 11 | echo go build -o ${PRO_ROOT}/imgsync -ldflags "${LDFLAGS}" ${PRO_ROOT}/main.go 12 | 13 | 14 | case "$1" in 15 | "release") # checkout到tag构建完再checkout回来 16 | bash ${PRO_ROOT}/build/lib/all-release.sh 17 | ;; 18 | "build") #使用master构建测试版本 19 | if [ -z `command -v go ` ];then 20 | echo go is not in PATH 21 | exit 1 22 | fi 23 | go build -o ${PRO_ROOT}/imgsync -tags=containers_image_openpgp -ldflags "${LDFLAGS}" ${PRO_ROOT}/main.go 24 | ;; 25 | "docker-local") #使用本地编译二进制文件打包docker和dist 26 | Dockerfile=Dockerfile.local 27 | go build -o ${PRO_ROOT}/imgsync -tags=containers_image_openpgp -ldflags "${LDFLAGS}" ${PRO_ROOT}/main.go 28 | ;& 29 | "docker") #使用容器编译和打包dist 30 | docker build -t zhangguanzhang/google_containers_sync:$TAG_NUM $build_arg \ 31 | --build-arg LDFLAGS="${LDFLAGS}" -f ${Dockerfile:=Dockerfile} . 32 | [ -n "${DockerUser}" ] && { 33 | docker login -u "${DockerUser}" "${DockerPass}" 34 | docker push zhangguanzhang/google_containers_sync:$TAG_NUM 35 | } 36 | ;; 37 | "clean") 38 | 39 | ;; 40 | *) 41 | echo -e "\t\033[1;31m must choose one to run \033[0m" 42 | exit 1 43 | ;; 44 | esac 45 | -------------------------------------------------------------------------------- /build/lib/var.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #GONELIST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)" 4 | readonly git=(git --work-tree "${PRO_ROOT}") 5 | readonly BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') 6 | readonly HEAD=$("${git[@]}" rev-parse "HEAD^{commit}") 7 | export GONELIST_ROOT BUILD_DATE HEAD 8 | 9 | 10 | GONELIST::getVersion(){ 11 | #指定tag版本时候判断存在否 12 | if [ -n "$TAG" ]; then 13 | TAG_COMMITID=$("${git[@]}" rev-parse $TAG 2>/dev/null) 14 | if [ "$?" -ne 0 ];then 15 | echo no such tag: $TAG 16 | exit 1 17 | fi 18 | else #默认取最新的tag 19 | TAG_COMMITID=$("${git[@]}" rev-list --tags --max-count=1) 20 | TAG=$("${git[@]}" describe --tags ${TAG_COMMITID}) 21 | fi 22 | 23 | if [ -z $TAG ];then 24 | TAG=v0.0 25 | fi 26 | 27 | "${git[@]}" checkout $TAG 2>/dev/null 28 | BUILD_VERSION=${TAG} 29 | 30 | if [ -z "$("${git[@]}" status --porcelain 2>/dev/null)" ];then 31 | GIT_TREE_STATE='clean' 32 | else 33 | GIT_TREE_STATE='dirty' 34 | fi 35 | 36 | #master切到tag版本则置为dirty 37 | 38 | if [ "${HEAD}" != "${TAG_COMMITID}" ];then 39 | #tag的基础上改动,所以tag版本号-dirty 40 | BUILD_VERSION+="-dirty" 41 | COMMIT_ID=${HEAD} 42 | else 43 | COMMIT_ID=${TAG_COMMITID} 44 | fi 45 | 46 | "${git[@]}" checkout $HEAD 2>/dev/null 47 | } 48 | 49 | GONELIST::SetVersion(){ 50 | GONELIST::getVersion &>/dev/null 51 | 52 | local -a ldflags 53 | function add_ldflag() { 54 | local key=${1} 55 | local val=${2} 56 | ldflags+=( 57 | "-X 'main.${key}=${val}'" 58 | ) 59 | } 60 | 61 | add_ldflag 'Version' ${BUILD_VERSION} 62 | add_ldflag 'buildDate' ${BUILD_DATE} 63 | add_ldflag 'gitCommit' ${COMMIT_ID} 64 | add_ldflag 'gitTreeState' ${GIT_TREE_STATE} 65 | 66 | unset TAG_COMMITID BUILD_VERSION COMMIT_ID GIT_TREE_STATE 67 | 68 | # The -ldflags parameter takes a single string, so join the output. 69 | echo $TAG "${ldflags[*]-}" 70 | 71 | } -------------------------------------------------------------------------------- /cmd/check.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | log "github.com/sirupsen/logrus" 7 | "github.com/spf13/cobra" 8 | "github.com/zhangguanzhang/google_containers/core" 9 | bolt "go.etcd.io/bbolt" 10 | "go/types" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | func NewCheckComamnd() *cobra.Command { 16 | var dbFile string 17 | cmd := &cobra.Command{ 18 | Use: "check", 19 | Short: "Check if the image needs to be synchronized", 20 | Args: cobra.ExactArgs(1), 21 | Run: func(cmd *cobra.Command, args []string) { 22 | db, err := bolt.Open(dbFile, 0600, &bolt.Options{ 23 | Timeout: 3 * time.Second, 24 | ReadOnly: true}) 25 | if err != nil { 26 | log.Fatalf("open the boltdb file %s error: %v", dbFile, err) 27 | } 28 | defer db.Close() 29 | 30 | if err := db.View(func(tx *bolt.Tx) error { 31 | return tx.ForEach(func(bName []byte, b *bolt.Bucket) error { 32 | c := b.Cursor() 33 | for k, v := c.First(); k != nil; k, v = c.Next() { 34 | if len(v) != int(types.Uint32) { 35 | log.Errorf("wrong: bucket:%s key=%s\n", bName, k) 36 | continue 37 | } 38 | 39 | if strings.Compare(fmt.Sprintf("%s/%s", bName, k), args[0]) == 0 { 40 | lValue := binary.LittleEndian.Uint32(v) 41 | rValue, err := core.GetManifestBodyCheckSum(args[0]) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | fmt.Printf("%s/%s local:%d remote:%d\n", bName, k, lValue, rValue) 46 | break 47 | } 48 | 49 | } 50 | return nil 51 | }) 52 | }); err != nil { 53 | log.Fatal(err) 54 | } 55 | }, 56 | } 57 | 58 | cmd.Flags().StringVar(&dbFile, "db", "bolt.db", "the bold db file.") 59 | 60 | return cmd 61 | } 62 | 63 | func NewReplaceComamnd() *cobra.Command { 64 | var dbFile string 65 | cmd := &cobra.Command{ 66 | Use: "replace", 67 | Short: "use the remote sum to replace the local db", 68 | Args: cobra.ExactArgs(1), 69 | Run: func(cmd *cobra.Command, args []string) { 70 | db, err := bolt.Open(dbFile, 0600, &bolt.Options{Timeout: 3 * time.Second}) 71 | if err != nil { 72 | log.Fatalf("open the boltdb file %s error: %v", dbFile, err) 73 | } 74 | defer db.Close() 75 | 76 | for _, image := range args { 77 | rValue, err := core.GetManifestBodyCheckSum(image) 78 | if err != nil { 79 | log.Errorf("%s|%v", image, err) 80 | } 81 | 82 | key := []byte(strings.TrimPrefix(image, "gcr.io")) 83 | if err := db.Update(func(tx *bolt.Tx) error { 84 | if err := tx.Bucket([]byte("gcr.io")).Delete(key); err != nil { 85 | return err 86 | } 87 | dstBytesBuf := make([]byte, types.Uint32) 88 | binary.LittleEndian.PutUint32(dstBytesBuf, rValue) 89 | if err = tx.Bucket([]byte("gcr.io")).Put(key, dstBytesBuf); err != nil { 90 | return err 91 | } 92 | return nil 93 | }); err != nil { 94 | log.Errorf("%s|%v", image, err) 95 | } 96 | } 97 | 98 | }, 99 | } 100 | 101 | cmd.Flags().StringVar(&dbFile, "db", "bolt.db", "the bold db file.") 102 | 103 | return cmd 104 | } 105 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | log "github.com/sirupsen/logrus" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | 12 | func NewImgSyncCommand() *cobra.Command { 13 | rootCmd := &cobra.Command{ 14 | Use: "imgsync", 15 | Short: "Docker image sync tool", 16 | Long: ` 17 | Docker image sync tool for k8s.gcr.io.`, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | _ = cmd.Help() 20 | }, 21 | } 22 | 23 | return rootCmd 24 | } 25 | 26 | 27 | func Execute() { 28 | var debug bool 29 | rootCmd := NewImgSyncCommand() 30 | initLog := func() { 31 | log.SetFormatter(&log.TextFormatter{ 32 | FullTimestamp: true, 33 | TimestampFormat: "2006-01-02 15:04:05", 34 | }) 35 | if debug { 36 | log.SetLevel(log.DebugLevel) 37 | 38 | } 39 | } 40 | cobra.OnInitialize(initLog) 41 | rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "debug mode") 42 | rootCmd.SetVersionTemplate(versionTpl()) 43 | rootCmd.AddCommand(NewSyncComamnd(nil), 44 | NewSumCommand(), 45 | NewGetSumCommand(), 46 | NewCheckComamnd(), 47 | NewReplaceComamnd(), 48 | ) 49 | 50 | if err := rootCmd.Execute(); err != nil { 51 | log.Fatal(err) 52 | } 53 | } 54 | 55 | 56 | 57 | 58 | 59 | 60 | var ( 61 | Version string 62 | gitCommit string 63 | gitTreeState = "" // state of git tree, either "clean" or "dirty" 64 | buildDate = "1970-01-01T00:00:00Z" // build date, output of $(date +'%Y-%m-%dT%H:%M:%S') 65 | ) 66 | 67 | func versionTpl() string { 68 | return fmt.Sprintf(`Name: imgsync 69 | Version: %s 70 | CommitID: %s 71 | GitTreeState: %s 72 | BuildDate: %s 73 | GoVersion: %s 74 | Compiler: %s 75 | Platform: %s/%s 76 | `, Version, gitCommit, gitTreeState, buildDate, runtime.Version(), runtime.Compiler, runtime.GOOS, runtime.GOARCH) 77 | } -------------------------------------------------------------------------------- /cmd/sum.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | bolt "github.com/etcd-io/bbolt" 7 | log "github.com/sirupsen/logrus" 8 | "github.com/spf13/cobra" 9 | "github.com/zhangguanzhang/google_containers/core" 10 | "go/types" 11 | "time" 12 | ) 13 | 14 | func NewSumCommand() *cobra.Command { 15 | return &cobra.Command{ 16 | Use: "sum", 17 | Short: "list all check sum", 18 | Args: cobra.ExactArgs(1), 19 | Run: listCheckSum, 20 | } 21 | } 22 | 23 | func listCheckSum(cmd *cobra.Command, args []string) { 24 | db, err := bolt.Open(args[0], 0600, &bolt.Options{ 25 | Timeout: 1 * time.Second, 26 | ReadOnly: true, 27 | }) 28 | if err != nil { 29 | log.Fatalf("open the boltdb file %s error: %v", args[0], err) 30 | } 31 | defer db.Close() 32 | if err := db.View(func(tx *bolt.Tx) error { 33 | return tx.ForEach(func(bName []byte, b *bolt.Bucket) error { 34 | c := b.Cursor() 35 | for k, v := c.First(); k != nil; k, v = c.Next() { 36 | if len(v) != int(types.Uint32) { 37 | fmt.Printf("wrong: bucket:%s key=%s\n", bName, k) 38 | continue 39 | } 40 | 41 | fmt.Printf("bucket:%-35s key=%-65s, value=%v\n", bName, k, binary.LittleEndian.Uint32(v)) 42 | } 43 | return nil 44 | }) 45 | }); err != nil { 46 | log.Fatal(err) 47 | } 48 | } 49 | 50 | func NewGetSumCommand() *cobra.Command { 51 | return &cobra.Command{ 52 | Use: "gsum", 53 | Short: "get Sum", 54 | Args: cobra.ExactArgs(1), 55 | Run: getSum, 56 | } 57 | } 58 | 59 | func getSum(cmd *cobra.Command, args []string) { 60 | for _, image := range args { 61 | crc32Value, err := core.GetManifestBodyCheckSum(image) 62 | if err != nil { 63 | log.Errorf("%s|%v", image, err) 64 | } 65 | fmt.Printf("%s | %d\n", image, crc32Value) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /cmd/sync.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/spf13/cobra" 7 | flag "github.com/spf13/pflag" 8 | "github.com/zhangguanzhang/google_containers/core" 9 | ) 10 | 11 | func NewSyncComamnd(Options *core.SyncOption) *cobra.Command { 12 | if Options == nil { 13 | Options = &core.SyncOption{} 14 | } 15 | 16 | cmd := &cobra.Command{ 17 | Use: "sync", 18 | Short: "Sync docker images", 19 | Long: ` 20 | Sync docker images.`, 21 | //Args: cobra.ExactArgs(1), 22 | PreRunE: Options.PreRun, 23 | Run: func(cmd *cobra.Command, args []string) { 24 | core.Run(Options) 25 | }, 26 | } 27 | 28 | AddSyncAuthFlags(cmd.Flags(), Options) 29 | AddSyncLimitFlags(cmd.Flags(), Options) 30 | 31 | return cmd 32 | } 33 | 34 | func AddSyncAuthFlags(flagSet *flag.FlagSet, op *core.SyncOption) { 35 | flagSet.StringVarP( 36 | &op.Auth.User, "user", "u", "", 37 | "The username to push.", 38 | ) 39 | flagSet.StringVarP( 40 | &op.Auth.Pass, "password", "p", "", 41 | "The password to push.", 42 | ) 43 | flagSet.StringVar( 44 | &op.PushRepo, "push-to", "docker.io", 45 | "the repo push to", 46 | ) 47 | flagSet.StringVar( 48 | &op.PushNS, "push-ns", "", 49 | "the ns push to", 50 | ) 51 | flagSet.Uint8Var( 52 | &op.LoginRetry, "login-retry", 2, 53 | "login retry when timeout.", 54 | ) 55 | } 56 | 57 | func AddSyncLimitFlags(flagSet *flag.FlagSet, op *core.SyncOption) { 58 | flagSet.StringVar( 59 | &op.DbFile, "db", "bolt.db", 60 | "the boltdb file", 61 | ) 62 | flagSet.IntVar( 63 | &op.QueryLimit, "query-limit", 10, 64 | "http query limit.", 65 | ) 66 | flagSet.IntVar( 67 | &op.Limit, "process-limit", 2, 68 | "sync process limit.", 69 | ) 70 | flagSet.DurationVar( 71 | &op.CmdTimeout, "command-timeout", 0, 72 | "timeout for the command execution.", 73 | ) 74 | flagSet.DurationVar( 75 | &op.SingleTimeout, "img-timeout", 15*time.Minute, 76 | "sync single image timeout.", 77 | ) 78 | flagSet.DurationVar( 79 | &op.LiveInterval, "live-interval", 0, 80 | "live output in ci-runner.", 81 | ) 82 | flagSet.IntVar( 83 | &op.Retry, "retry", 4, 84 | "retry count while err.", 85 | ) 86 | flagSet.DurationVar( 87 | &op.RetryInterval, "retry-interval", 4*time.Second, 88 | "retry interval while err.", 89 | ) 90 | flagSet.StringArrayVar( 91 | &op.AdditionNS, "addition-ns", nil, 92 | "addition ns to sync") 93 | } 94 | -------------------------------------------------------------------------------- /core/checksum.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "go/types" 7 | "os" 8 | 9 | bolt "github.com/etcd-io/bbolt" 10 | log "github.com/sirupsen/logrus" 11 | ) 12 | 13 | type CheckSumer interface { 14 | CreatBucket(string) error 15 | Diff(string, uint32) (bool, error) 16 | Save(string, uint32) error 17 | } 18 | 19 | type boltdb struct { 20 | db *bolt.DB 21 | bucketName string // current bucket name 22 | } 23 | 24 | func NewBolt(db *bolt.DB) CheckSumer { 25 | return &boltdb{db: db} 26 | } 27 | 28 | func (b *boltdb) Bucket(tx *bolt.Tx) *bolt.Bucket { 29 | return tx.Bucket([]byte(b.bucketName)) 30 | } 31 | 32 | func (b *boltdb) CreatBucket(domain string) error { 33 | return b.db.Update(func(tx *bolt.Tx) error { 34 | var err error 35 | _, err = tx.CreateBucketIfNotExists([]byte(domain)) 36 | if err != nil { 37 | return fmt.Errorf("create bucket failed: %s", err) 38 | } 39 | b.bucketName = domain 40 | return nil 41 | }) 42 | } 43 | 44 | // imageName是镜像名去掉域名部分带tag remoteSum 45 | func (b *boltdb) Diff(imageName string, remoteSum uint32) (bool, error) { 46 | var ( 47 | err error 48 | SumBytes []byte 49 | ) 50 | 51 | err = b.db.View(func(tx *bolt.Tx) error { 52 | SumBytes = b.Bucket(tx).Get([]byte(imageName)) 53 | return nil 54 | }) 55 | 56 | if err != nil { 57 | return false, err 58 | } 59 | 60 | if len(SumBytes) != int(types.Uint32) { //没读到数据或者,长度不对下不能使用binary的方法转uint32,大于4字节会out of range 61 | log.Debugf("imageName:%s, len:%d", imageName, len(SumBytes)) 62 | return true, nil 63 | } 64 | 65 | lsum := binary.LittleEndian.Uint32(SumBytes) //和下面的Save同时使用小端或者大端 66 | if remoteSum != lsum { 67 | if os.Getenv("HASH_DIS") != "" { //环境变量来debug输出 68 | log.Infof("imageName:%s local:%d remote:%d", imageName, remoteSum, lsum) 69 | } 70 | return true, nil 71 | } 72 | 73 | return false, err 74 | } 75 | 76 | func (b *boltdb) Save(imageName string, checkSum uint32) error { 77 | dstBytesBuf := make([]byte, types.Uint32) 78 | binary.LittleEndian.PutUint32(dstBytesBuf, checkSum) 79 | return b.db.Update(func(tx *bolt.Tx) error { 80 | return b.Bucket(tx).Put([]byte(imageName), dstBytesBuf) 81 | }) 82 | } 83 | -------------------------------------------------------------------------------- /core/gcr.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | "github.com/containers/image/v5/docker" 10 | "github.com/containers/image/v5/types" 11 | jsoniter "github.com/json-iterator/go" 12 | "github.com/panjf2000/ants/v2" 13 | "github.com/parnurzeal/gorequest" 14 | log "github.com/sirupsen/logrus" 15 | ) 16 | 17 | const ( 18 | imgList = "https://k8s.gcr.io/v2/tags/list" 19 | DefaultHTTPTimeout = 15 * time.Second 20 | repo = "k8s.gcr.io/" 21 | ) 22 | 23 | 24 | // baseName,不是full name 25 | func NSImages(op *SyncOption) ([]string, error) { 26 | log.Info("get k8s.gcr.io public images...") 27 | resp, body, errs := gorequest.New(). 28 | Timeout(DefaultHTTPTimeout). 29 | Retry(op.Retry, op.RetryInterval). 30 | Get(imgList). 31 | EndBytes() 32 | if errs != nil { 33 | return nil, fmt.Errorf("%s", errs) 34 | } 35 | 36 | defer func() { _ = resp.Body.Close() }() 37 | 38 | var imageNames []string 39 | err := jsoniter.UnmarshalFromString(jsoniter.Get(body, "child").ToString(), &imageNames) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | if len(op.AdditionNS) > 0 { 45 | log.Debugf("AdditionNS: %v", op.AdditionNS) 46 | } 47 | 48 | for _, v := range op.AdditionNS { 49 | resp, body, errs := gorequest.New(). 50 | Timeout(DefaultHTTPTimeout). 51 | Retry(op.Retry, op.RetryInterval). 52 | Get(fmt.Sprintf("https://k8s.gcr.io/v2/%s/tags/list", v)). 53 | EndBytes() 54 | if errs != nil { 55 | log.Errorf("%s", errs) 56 | continue 57 | } 58 | 59 | defer func() { _ = resp.Body.Close() }() 60 | 61 | nsImageNames := []string{} 62 | err := jsoniter.UnmarshalFromString(jsoniter.Get(body, "child").ToString(), &nsImageNames) 63 | if err != nil { 64 | log.Error(errs) 65 | continue 66 | } 67 | for k := range nsImageNames { 68 | nsImageNames[k] = v + "/" + nsImageNames[k] 69 | } 70 | imageNames = append(imageNames, nsImageNames...) 71 | } 72 | 73 | return imageNames, nil 74 | } 75 | 76 | //并发获取k8s.gcr.io/$imgName:tag写入chan 77 | func ImageNames(opt *SyncOption) (Images, error) { 78 | 79 | publicImageNames, err := NSImages(opt) 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | log.Infof("sync ns count: %d in k8s.gcr.io", len(publicImageNames)) 85 | 86 | pool, err := ants.NewPool(opt.QueryLimit, ants.WithPreAlloc(true), ants.WithPanicHandler(func(i interface{}) { 87 | log.Error(i) 88 | })) 89 | if err != nil { 90 | log.Fatalf("failed to create goroutines pool: %s", err) 91 | } 92 | 93 | //并发写入镜像信息结构体 94 | var images Images 95 | imgCh := make(chan Image, opt.QueryLimit) 96 | err = pool.Submit(func() { 97 | for image := range imgCh { 98 | img := image 99 | images = append(images, &img) 100 | } 101 | }) 102 | if err != nil { 103 | log.Fatalf("failed to submit task: %s", err) 104 | } 105 | 106 | 107 | imgGetWg := new(sync.WaitGroup) 108 | imgGetWg.Add(len(publicImageNames)) 109 | 110 | for _, tmpImageName := range publicImageNames { 111 | imageBaseName := tmpImageName 112 | var iName string 113 | iName = repo + imageBaseName 114 | 115 | //协程池提交生产者写入channel 116 | err = pool.Submit(func() { 117 | defer imgGetWg.Done() 118 | select { 119 | case <-opt.Ctx.Done(): 120 | log.Debugf("context done exit while %s", iName) 121 | default: 122 | log.Debugf("query image [%s] tags...", iName) 123 | tags, err := getImageTags(iName, TagsOption{ 124 | ctx: opt.Ctx, 125 | Timeout: time.Second * 25, 126 | }) 127 | if err != nil { 128 | log.Errorf("failed to get image [%s] tags, error: %s", iName, err) 129 | return 130 | } 131 | log.Debugf("image [%s] tags count: %d", iName, len(tags)) 132 | //构建带tag的镜像名 133 | 134 | for _, tag := range tags { 135 | imgCh <- Image{ 136 | Name: imageBaseName, 137 | Tag: tag, 138 | } 139 | } 140 | } 141 | }) 142 | if err != nil { 143 | log.Fatalf("failed to submit task: %s while %s", err, iName) 144 | } 145 | } 146 | imgGetWg.Wait() 147 | log.Infof("Complete the tag of all images, total:%d", len(images)) 148 | pool.Release() 149 | close(imgCh) 150 | return images, nil 151 | } 152 | 153 | 154 | func getImageTags(imageName string, opt TagsOption) ([]string, error) { 155 | srcRef, err := docker.ParseReference("//" + imageName) 156 | if err != nil { 157 | return nil, err 158 | } 159 | sourceCtx := &types.SystemContext{DockerAuthConfig: &types.DockerAuthConfig{}} 160 | tagsCtx, tagsCancel := context.WithTimeout(opt.ctx, opt.Timeout) 161 | defer tagsCancel() 162 | return docker.GetRepositoryTags(tagsCtx, sourceCtx, srcRef) 163 | } 164 | 165 | 166 | func retry(count int, interval time.Duration, f func() error) error { 167 | var err error 168 | for ; count > 0; count-- { 169 | if err = f(); err != nil { 170 | if interval > 0 { 171 | <-time.After(interval) 172 | } 173 | } else { 174 | break 175 | } 176 | } 177 | return err 178 | } 179 | -------------------------------------------------------------------------------- /core/manifests.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "hash/crc32" 7 | 8 | "github.com/containers/image/v5/docker" 9 | "github.com/containers/image/v5/manifest" 10 | "github.com/containers/image/v5/types" 11 | imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" 12 | ) 13 | 14 | // crc32 hash and err 15 | func GetManifestBodyCheckSum(imageName string) (uint32, error) { 16 | srcRef, err := docker.ParseReference("//" + imageName) 17 | if err != nil { 18 | return 0, err 19 | } 20 | 21 | sourceCtx := &types.SystemContext{DockerAuthConfig: &types.DockerAuthConfig{}} 22 | imageSrcCtx, imageSrcCancel := context.WithTimeout(context.Background(), DefaultCtxTimeout) 23 | defer imageSrcCancel() 24 | src, err := srcRef.NewImageSource(imageSrcCtx, sourceCtx) 25 | if err != nil { 26 | return 0, err 27 | } 28 | 29 | getManifestCtx, getManifestCancel := context.WithTimeout(context.Background(), DefaultCtxTimeout) 30 | defer getManifestCancel() 31 | mbs, _, err := src.GetManifest(getManifestCtx, nil) 32 | if err != nil { 33 | return 0, err 34 | } 35 | 36 | mType := manifest.GuessMIMEType(mbs) 37 | if mType == "" { 38 | return 0, fmt.Errorf("faile to parse image [%s] manifest type", imageName) 39 | } 40 | 41 | if mType != manifest.DockerV2ListMediaType && mType != imgspecv1.MediaTypeImageIndex { 42 | _, err = manifest.FromBlob(mbs, mType) 43 | if err != nil { 44 | return 0, err 45 | } 46 | } 47 | 48 | return crc32.ChecksumIEEE(mbs), nil 49 | 50 | } 51 | -------------------------------------------------------------------------------- /core/option.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | types2 "github.com/docker/docker/api/types" 6 | "github.com/docker/docker/registry" 7 | bolt "github.com/etcd-io/bbolt" 8 | "github.com/pkg/errors" 9 | log "github.com/sirupsen/logrus" 10 | "github.com/spf13/cobra" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | type auth struct { 16 | User string `json:"user" yaml:"user"` 17 | Pass string `json:"pass" yaml:"pass"` 18 | } 19 | 20 | type SyncOption struct { 21 | Auth auth 22 | CmdTimeout time.Duration // command timeout 23 | SingleTimeout time.Duration // Sync single image timeout 24 | LiveInterval time.Duration 25 | LoginRetry uint8 26 | Limit int // Images sync process limit 27 | PushRepo string 28 | PushNS string 29 | QueryLimit int // Query Gcr images limit 30 | Retry int 31 | RetryInterval time.Duration 32 | Report bool // Report sync result 33 | Ctx context.Context 34 | CheckSumer 35 | Closer func() error 36 | DbFile string 37 | AdditionNS []string 38 | } 39 | 40 | func (s *SyncOption) PreRun(cmd *cobra.Command, args []string) error { 41 | 42 | if s.Auth.User == "" && s.Auth.Pass == "" { 43 | log.Fatal("--user or --password must not be empty") 44 | } 45 | if s.PushNS == "" { 46 | s.PushNS = s.Auth.User 47 | } 48 | 49 | 50 | if err := s.Verify(); err != nil { 51 | return err 52 | } 53 | 54 | log.Infof("Login Succeeded for %s", s.PushRepo) 55 | 56 | db, err := bolt.Open(s.DbFile, 0660, &bolt.Options{Timeout: 10 * time.Second}) 57 | if err != nil { 58 | return errors.Wrapf(err, "open the boltdb file %s", s.DbFile) 59 | } 60 | s.Closer = db.Close 61 | s.CheckSumer = NewBolt(db) 62 | return nil 63 | } 64 | 65 | func (s *SyncOption) setDefault() *SyncOption { 66 | if s.QueryLimit < 2 { 67 | s.QueryLimit = 2 68 | } 69 | if s.Limit < 2 { 70 | s.Limit = 2 71 | } 72 | return s 73 | } 74 | 75 | func (s *SyncOption) Verify() error { 76 | authConf := &types2.AuthConfig{ 77 | Username: s.Auth.User, 78 | Password: s.Auth.Pass, 79 | } 80 | 81 | // https://github.com/moby/moby/blob/c3b3aedfa4ad51de0a2ebfd08a716f585390b512/daemon/daemon.go#L714 82 | // https://github.com/moby/moby/blob/master/daemon/auth.go 83 | 84 | if s.PushRepo == registry.IndexName { 85 | authConf.ServerAddress = registry.IndexServer 86 | } else { 87 | authConf.ServerAddress = s.PushRepo 88 | } 89 | if !strings.HasPrefix(authConf.ServerAddress, "https://") && !strings.HasPrefix(authConf.ServerAddress, "http://") { 90 | authConf.ServerAddress = "https://" + authConf.ServerAddress 91 | } 92 | 93 | RegistryService, err := registry.NewService(registry.ServiceOptions{}) 94 | if err != nil { 95 | return err 96 | } 97 | 98 | var ( 99 | status string 100 | errContains = []string{"imeout", "dead"} 101 | ) 102 | 103 | for count := s.LoginRetry; count > 0; count-- { 104 | status, _, err = RegistryService.Auth(s.Ctx, authConf, "") 105 | if err != nil && contains(errContains, err.Error()) { 106 | <-time.After(time.Second * 1) 107 | } else { 108 | break 109 | } 110 | } 111 | 112 | if err != nil { 113 | return err 114 | } 115 | 116 | if !strings.Contains(status, "Succeeded") { 117 | return errors.New("cannot get status") 118 | } 119 | return nil 120 | } 121 | 122 | 123 | func contains(s []string, searchterm string) bool { 124 | for _, v := range s { 125 | if strings.Contains(searchterm, v) { 126 | return true 127 | } 128 | } 129 | return false 130 | } -------------------------------------------------------------------------------- /core/sync.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "os/signal" 8 | "sort" 9 | "sync" 10 | "syscall" 11 | "time" 12 | 13 | "github.com/pkg/errors" 14 | 15 | "github.com/containers/image/v5/copy" 16 | "github.com/containers/image/v5/docker" 17 | "github.com/containers/image/v5/signature" 18 | "github.com/containers/image/v5/types" 19 | "github.com/panjf2000/ants/v2" 20 | log "github.com/sirupsen/logrus" 21 | ) 22 | 23 | func Run(opt *SyncOption) { 24 | 25 | opt = opt.setDefault() 26 | 27 | defer func() { 28 | _ = opt.Closer() 29 | }() 30 | Sigs := make(chan os.Signal) 31 | 32 | var cancel context.CancelFunc 33 | opt.Ctx, cancel = context.WithCancel(context.Background()) 34 | if opt.CmdTimeout > 0 { 35 | opt.Ctx, cancel = context.WithTimeout(opt.Ctx, opt.CmdTimeout) 36 | } 37 | 38 | var cancelOnce sync.Once 39 | defer cancel() 40 | go func() { 41 | for range Sigs { 42 | cancelOnce.Do(func() { 43 | log.Info("Receiving a termination signal, gracefully shutdown!") 44 | cancel() 45 | }) 46 | log.Info("The goroutines pool has stopped, please wait for the remaining tasks to complete.") 47 | } 48 | }() 49 | signal.Notify(Sigs, syscall.SIGINT, syscall.SIGTERM) 50 | 51 | if err := opt.CheckSumer.CreatBucket("k8s.gcr.io"); err != nil { 52 | log.Error(err) 53 | } 54 | 55 | if opt.LiveInterval > 0 { 56 | if opt.LiveInterval >= 10*time.Minute { //travis-ci 10分钟没任何输出就会被强制关闭 57 | opt.LiveInterval = 9 * time.Minute 58 | } 59 | go func() { 60 | for { 61 | select { 62 | case <-opt.Ctx.Done(): 63 | return 64 | case <-time.After(opt.LiveInterval): 65 | log.Info("Live output for live in ci runner") 66 | } 67 | } 68 | }() 69 | } 70 | // 71 | //for _, ns := range namespace { 72 | // g.Sync(ns) 73 | //} 74 | if err := Sync(opt); err != nil { 75 | log.Fatal(err) 76 | } 77 | } 78 | 79 | func Sync(opt *SyncOption) error { 80 | allImages, err := ImageNames(opt) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | imgs := SyncImages(allImages, opt) 86 | log.Info("sync done") 87 | report(imgs) 88 | return nil 89 | } 90 | 91 | func SyncImages(imgs Images, opt *SyncOption) Images { 92 | 93 | processWg := new(sync.WaitGroup) 94 | processWg.Add(len(imgs)) 95 | 96 | if opt.Limit == 0 { 97 | opt.Limit = DefaultLimit 98 | } 99 | 100 | pool, err := ants.NewPool(opt.Limit, ants.WithPreAlloc(true), ants.WithPanicHandler(func(i interface{}) { 101 | log.Error(i) 102 | })) 103 | 104 | if err != nil { 105 | log.Fatalf("failed to create goroutines pool: %s", err) 106 | } 107 | sort.Sort(imgs) 108 | for i := 0; i < len(imgs); i++ { 109 | k := i 110 | err = pool.Submit(func() { 111 | defer processWg.Done() 112 | 113 | select { 114 | case <-opt.Ctx.Done(): 115 | default: 116 | log.Debug("process image: ", imgs[k].String()) 117 | newSum, needSync := checkSync(imgs[k], opt) 118 | if !needSync { 119 | return 120 | } 121 | 122 | rerr := retry(opt.Retry, opt.RetryInterval, func() error { 123 | return sync2DockerHub(imgs[k], opt) 124 | }) 125 | if rerr != nil { 126 | imgs[k].Err = rerr 127 | log.Errorf("failed to process image %s, error: %s", imgs[k].String(), rerr) 128 | return 129 | } 130 | imgs[k].Success = true 131 | 132 | //写入校验值 133 | if sErr := opt.CheckSumer.Save(imgs[k].Key(), newSum); sErr != nil { 134 | log.Errorf("failed to save image [%s] checksum: %s", imgs[k].String(), sErr) 135 | } 136 | log.Debugf("save image [%s] checksum: %d", imgs[k].String(), newSum) 137 | } 138 | }) 139 | if err != nil { 140 | log.Fatalf("failed to submit task: %s", err) 141 | } 142 | } 143 | processWg.Wait() 144 | pool.Release() 145 | return imgs 146 | } 147 | 148 | func sync2DockerHub(image *Image, opt *SyncOption) error { 149 | 150 | srcImg := image.String() 151 | destImg := fmt.Sprintf("%s/%s/%s:%s", opt.PushRepo, opt.PushNS, image.Name, image.Tag) 152 | 153 | log.Infof("syncing %s => %s", srcImg, destImg) 154 | 155 | ctx, cancel := context.WithTimeout(opt.Ctx, opt.SingleTimeout) 156 | defer cancel() 157 | 158 | policyContext, err := signature.NewPolicyContext( 159 | &signature.Policy{ 160 | Default: []signature.PolicyRequirement{signature.NewPRInsecureAcceptAnything()}, 161 | }, 162 | ) 163 | 164 | if err != nil { 165 | return err 166 | } 167 | defer func() { _ = policyContext.Destroy() }() 168 | 169 | srcRef, err := docker.ParseReference("//" + srcImg) 170 | if err != nil { 171 | return err 172 | } 173 | destRef, err := docker.ParseReference("//" + destImg) 174 | if err != nil { 175 | return err 176 | } 177 | 178 | sourceCtx := &types.SystemContext{DockerAuthConfig: &types.DockerAuthConfig{}} 179 | destinationCtx := &types.SystemContext{DockerAuthConfig: &types.DockerAuthConfig{ 180 | Username: opt.Auth.User, 181 | Password: opt.Auth.Pass, 182 | }} 183 | 184 | log.Debugf("copy %s to %s ...", image.String(), opt.PushRepo) 185 | _, err = copy.Image(ctx, policyContext, destRef, srcRef, ©.Options{ 186 | SourceCtx: sourceCtx, 187 | DestinationCtx: destinationCtx, 188 | ImageListSelection: copy.CopyAllImages, 189 | }) 190 | log.Debugf("%s copy done, error is %v.", srcImg, err) 191 | return err 192 | } 193 | 194 | //已经同步过了没 195 | func checkSync(image *Image, opt *SyncOption) (uint32, bool) { 196 | var ( 197 | bodySum uint32 198 | diff bool 199 | ) 200 | imgFullName := image.String() 201 | err := retry(opt.Retry, opt.RetryInterval, func() error { 202 | var mErr error 203 | bodySum, mErr = GetManifestBodyCheckSum(imgFullName) 204 | if mErr != nil { 205 | return mErr 206 | } 207 | if bodySum == 0 { 208 | return errors.New("checkSum is 0, maybe resp body is nil") 209 | } 210 | return nil 211 | }) 212 | 213 | if err != nil { 214 | image.Err = err 215 | log.Errorf("failed to get image [%s] manifest, error: %s", imgFullName, err) 216 | return 0, false 217 | } 218 | 219 | // db查询校验值是否相等,只同步一个ns下镜像,所以bucket的key只用baseName:tag 220 | diff, err = opt.CheckSumer.Diff(image.Key(), bodySum) 221 | if err != nil { 222 | image.Err = err 223 | log.Errorf("failed to get image [%s] checkSum, error: %s", imgFullName, err) 224 | return 0, false 225 | } 226 | 227 | log.Debugf("%s diff:%v", imgFullName, diff) 228 | 229 | if !diff { //相同 230 | image.Success = true 231 | image.CacheHit = true 232 | log.Debugf("image [%s] not changed, skip sync...", imgFullName) 233 | return 0, false 234 | } 235 | 236 | return bodySum, true 237 | } 238 | 239 | func report(images Images) { 240 | 241 | var successCount, failedCount, cacheHitCount int 242 | var report string 243 | 244 | for _, img := range images { 245 | if img.Success { 246 | successCount++ 247 | if img.CacheHit { 248 | cacheHitCount++ 249 | } 250 | } else { 251 | failedCount++ 252 | } 253 | } 254 | report = fmt.Sprintf(`======================================== 255 | >> Sync Repo: k8s.gcr.io 256 | >> Sync Total: %d 257 | >> Sync Failed: %d 258 | >> Sync Success: %d 259 | >> CacheHit: %d`, len(images), failedCount, successCount, cacheHitCount) 260 | fmt.Println(report) 261 | } 262 | -------------------------------------------------------------------------------- /core/types.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type Image struct { 10 | Name string // basename 11 | Tag string 12 | 13 | Success bool 14 | CacheHit bool 15 | Err error 16 | } 17 | 18 | func (img *Image) String() string { 19 | return fmt.Sprintf("k8s.gcr.io/%s:%s", img.Name, img.Tag) 20 | } 21 | 22 | // used for bolt.db 23 | func (img *Image) Key() string { 24 | return fmt.Sprintf("%s:%s", img.Name, img.Tag) 25 | } 26 | 27 | type Images []*Image 28 | 29 | func (imgs Images) Len() int { return len(imgs) } 30 | func (imgs Images) Less(i, j int) bool { return imgs[i].String() < imgs[j].String() } 31 | func (imgs Images) Swap(i, j int) { imgs[i], imgs[j] = imgs[j], imgs[i] } 32 | 33 | 34 | type TagsOption struct { 35 | ctx context.Context 36 | Timeout time.Duration 37 | } 38 | 39 | const ( 40 | DefaultCtxTimeout = 5 * time.Minute 41 | DefaultLimit = 20 42 | ) 43 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zhangguanzhang/google_containers 2 | 3 | go 1.14 4 | 5 | //github.com/Sirupsen/logrus v1.6.0 => github.com/sirupsen/logrus v1.6.0 6 | replace github.com/etcd-io/bbolt v1.3.5 => go.etcd.io/bbolt v1.3.5 7 | 8 | require ( 9 | github.com/VividCortex/ewma v1.1.1 // indirect 10 | github.com/containerd/containerd v1.3.7 // indirect 11 | github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe // indirect 12 | github.com/containers/image/v5 v5.5.1 13 | github.com/containers/storage v1.23.0 // indirect 14 | github.com/docker/distribution v2.7.1+incompatible // indirect 15 | github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible 16 | github.com/docker/docker-credential-helpers v0.6.3 // indirect 17 | github.com/docker/go-connections v0.4.0 // indirect 18 | github.com/docker/go-metrics v0.0.1 // indirect 19 | github.com/docker/go-units v0.4.0 // indirect 20 | github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect 21 | github.com/etcd-io/bbolt v1.3.5 22 | github.com/gorilla/mux v1.7.4 // indirect 23 | github.com/json-iterator/go v1.1.10 24 | github.com/mattn/go-isatty v0.0.12 // indirect 25 | github.com/morikuni/aec v1.0.0 // indirect 26 | github.com/opencontainers/go-digest v1.0.0 // indirect 27 | github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 28 | github.com/panjf2000/ants/v2 v2.4.1 29 | github.com/parnurzeal/gorequest v0.2.16 30 | github.com/pkg/errors v0.9.1 31 | github.com/sirupsen/logrus v1.6.0 32 | github.com/spf13/cobra v1.0.0 33 | github.com/spf13/pflag v1.0.5 34 | github.com/ulikunitz/xz v0.5.7 // indirect 35 | github.com/vbatts/tar-split v0.11.1 // indirect 36 | github.com/vbauerster/mpb v3.4.0+incompatible // indirect 37 | go.etcd.io/bbolt v1.3.4 38 | //go.etcd.io/bbolt v1.3.5 39 | moul.io/http2curl v1.0.0 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= 2 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0= 4 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= 5 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 6 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 7 | github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= 8 | github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= 9 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 10 | github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= 11 | github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= 12 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= 13 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= 14 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 15 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 16 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 17 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 18 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 19 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 20 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 21 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 22 | github.com/checkpoint-restore/go-criu/v4 v4.0.2/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= 23 | github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s= 24 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 25 | github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= 26 | github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= 27 | github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= 28 | github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 29 | github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 30 | github.com/containerd/containerd v1.3.7 h1:eFSOChY8TTcxvkzp8g+Ov1RL0MYww7XEeK0y+zqGpVc= 31 | github.com/containerd/containerd v1.3.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 32 | github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= 33 | github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe h1:PEmIrUvwG9Yyv+0WKZqjXfSFDeZjs/q15g0m08BYS9k= 34 | github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= 35 | github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= 36 | github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= 37 | github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= 38 | github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= 39 | github.com/containers/image v1.5.1 h1:ssEuj1c24uJvdMkUa2IrawuEFZBP12p6WzrjNBTQxE0= 40 | github.com/containers/image v3.0.2+incompatible h1:B1lqAE8MUPCrsBLE86J0gnXleeRq8zJnQryhiiGQNyE= 41 | github.com/containers/image v3.0.2+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= 42 | github.com/containers/image/v5 v5.5.1 h1:h1FCOXH6Ux9/p/E4rndsQOC4yAdRU0msRTfLVeQ7FDQ= 43 | github.com/containers/image/v5 v5.5.1/go.mod h1:4PyNYR0nwlGq/ybVJD9hWlhmIsNra4Q8uOQX2s6E2uM= 44 | github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= 45 | github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= 46 | github.com/containers/ocicrypt v1.0.2 h1:Q0/IPs8ohfbXNxEfyJ2pFVmvJu5BhqJUAmc6ES9NKbo= 47 | github.com/containers/ocicrypt v1.0.2/go.mod h1:nsOhbP19flrX6rE7ieGFvBlr7modwmNjsqWarIUce4M= 48 | github.com/containers/storage v1.20.2/go.mod h1:oOB9Ie8OVPojvoaKWEGSEtHbXUAs+tSyr7RO7ZGteMc= 49 | github.com/containers/storage v1.23.0 h1:gYyNkBiihC2FvGiHOjOjpnfojYwgxpLVooTUlmD6pxs= 50 | github.com/containers/storage v1.23.0/go.mod h1:I1EIAA7B4OwWRSA0b4yq2AW1wjvvfcY0zLWQuwTa4zw= 51 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 52 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 53 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 54 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 55 | github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= 56 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 57 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 58 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 59 | github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= 60 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 61 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 62 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 63 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 64 | github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= 65 | github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 66 | github.com/docker/docker v1.4.2-0.20191219165747-a9416c67da9f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 67 | github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8= 68 | github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 69 | github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= 70 | github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= 71 | github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= 72 | github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= 73 | github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= 74 | github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= 75 | github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= 76 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 77 | github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= 78 | github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= 79 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 80 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 81 | github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= 82 | github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= 83 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 84 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 85 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 86 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 87 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 88 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 89 | github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= 90 | github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 91 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 92 | github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= 93 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 94 | github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= 95 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= 96 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 97 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 98 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 99 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 100 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 101 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 102 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 103 | github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= 104 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 105 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 106 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 107 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 108 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 109 | github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= 110 | github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 111 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 112 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 113 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 114 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 115 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 116 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 117 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= 118 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 119 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 120 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 121 | github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 122 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 123 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 124 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 125 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 126 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 127 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 128 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 129 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 130 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 131 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 132 | github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 133 | github.com/klauspost/compress v1.10.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 134 | github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I= 135 | github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 136 | github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A= 137 | github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 138 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 139 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 140 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 141 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 142 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 143 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 144 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 145 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 146 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 147 | github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= 148 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 149 | github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= 150 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 151 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 152 | github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= 153 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 154 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 155 | github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= 156 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 157 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 158 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 159 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 160 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 161 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 162 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 163 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 164 | github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= 165 | github.com/mtrmac/gpgme v0.1.2/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgiVFNI= 166 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 167 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 168 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 169 | github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 170 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 171 | github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 172 | github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 173 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 174 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 175 | github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= 176 | github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 177 | github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 h1:yN8BPXVwMBAm3Cuvh1L5XE8XpvYRMdsVLd82ILprhUU= 178 | github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= 179 | github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= 180 | github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= 181 | github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= 182 | github.com/opencontainers/runc v1.0.0-rc90/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= 183 | github.com/opencontainers/runc v1.0.0-rc91 h1:Tp8LWs5G8rFpzTsbRjAtQkPVexhCu0bnANE5IfIhJ6g= 184 | github.com/opencontainers/runc v1.0.0-rc91/go.mod h1:3Sm6Dt7OT8z88EbdQqqcRN2oCT54jbi72tT/HqgflT8= 185 | github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 186 | github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 187 | github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 188 | github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= 189 | github.com/opencontainers/selinux v1.5.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= 190 | github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= 191 | github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= 192 | github.com/panjf2000/ants v1.3.0 h1:8pQ+8leaLc9lys2viEEr8md0U4RN6uOSUCE9bOYjQ9M= 193 | github.com/panjf2000/ants/v2 v2.4.1 h1:7RtUqj5lGOw0WnZhSKDZ2zzJhaX5490ZW1sUolRXCxY= 194 | github.com/panjf2000/ants/v2 v2.4.1/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= 195 | github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ= 196 | github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= 197 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 198 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 199 | github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 200 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 201 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 202 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 203 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 204 | github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= 205 | github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= 206 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 207 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 208 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 209 | github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= 210 | github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= 211 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 212 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= 213 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 214 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 215 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 216 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 217 | github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= 218 | github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= 219 | github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 220 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 221 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 222 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 223 | github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= 224 | github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 225 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 226 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 227 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 228 | github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= 229 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 230 | github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= 231 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 232 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 233 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 234 | github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= 235 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 236 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 237 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 238 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 239 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 240 | github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 241 | github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= 242 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 243 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 244 | github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 245 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 246 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 247 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 248 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 249 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 250 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 251 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 252 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 253 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 254 | github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 255 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 256 | github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= 257 | github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= 258 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 259 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 260 | github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4= 261 | github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 262 | github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 263 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 264 | github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= 265 | github.com/vbauerster/mpb v1.1.3 h1:IRgic8VFaURXkW0VxDLkNOiNaAgtw0okB2YIaVvJDI4= 266 | github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw= 267 | github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= 268 | github.com/vbauerster/mpb/v5 v5.2.2 h1:zIICVOm+XD+uV6crpSORaL6I0Q1WqOdvxZTp+r3L9cw= 269 | github.com/vbauerster/mpb/v5 v5.2.2/go.mod h1:W5Fvgw4dm3/0NhqzV8j6EacfuTe5SvnzBRwiXxDR9ww= 270 | github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= 271 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= 272 | github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= 273 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 274 | github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 275 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 276 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 277 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 278 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 279 | go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= 280 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 281 | go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= 282 | go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= 283 | go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= 284 | go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= 285 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 286 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 287 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 288 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 289 | golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 290 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 291 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 292 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 293 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 294 | golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= 295 | golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 296 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 297 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 298 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 299 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 300 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 301 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 302 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 303 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 304 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 305 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 306 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 307 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 308 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 309 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 310 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= 311 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 312 | golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= 313 | golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 314 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= 315 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 316 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 317 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 318 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 319 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= 320 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 321 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 322 | golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= 323 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 324 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= 325 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 326 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 327 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 328 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 329 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 330 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 331 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 332 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 333 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 334 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 335 | golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 336 | golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 337 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= 338 | golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 339 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 340 | golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 341 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 342 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 343 | golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 344 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 345 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 346 | golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE= 347 | golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 348 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= 349 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 350 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 351 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 352 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 353 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 354 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 355 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 356 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 357 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 358 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 359 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 360 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 361 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 362 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 363 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 364 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= 365 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 366 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 367 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg= 368 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 369 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 370 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 371 | google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= 372 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 373 | google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk= 374 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 375 | google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= 376 | google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= 377 | gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 378 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 379 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 380 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 381 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 382 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 383 | gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= 384 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 385 | gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= 386 | gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 387 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 388 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 389 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 390 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 391 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 392 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 393 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 394 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 395 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 396 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 397 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 398 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 399 | moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= 400 | moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= 401 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/zhangguanzhang/google_containers/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } --------------------------------------------------------------------------------