mirror of
https://github.com/devplayer0/docker-net-dhcp
synced 2024-10-30 09:20:28 +00:00
commit
c80d3eee04
40
.github/workflows/build.yaml
vendored
Normal file
40
.github/workflows/build.yaml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- golang
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
env:
|
||||
PLATFORMS: linux/amd64,linux/386,linux/arm64,linux/arm/v7
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Install script dependencies
|
||||
run: pip3 install -r scripts/requirements.txt
|
||||
|
||||
- id: vars
|
||||
name: Generate build vars
|
||||
run: |
|
||||
SHA="${{ github.sha }}"
|
||||
echo "::set-output name=tag::"${SHA:0:8}-$(date +%s)Z""
|
||||
|
||||
- name: Build
|
||||
run: make PLUGIN_TAG=${{ steps.vars.outputs.tag }} multiarch
|
||||
|
||||
- name: Push
|
||||
env:
|
||||
REGISTRY_USERNAME: ${{ github.actor }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: make PLUGIN_TAG=${{ steps.vars.outputs.tag }} push-multiarch
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +1,6 @@
|
||||
/bin/*
|
||||
!/bin/.gitkeep
|
||||
/plugin/
|
||||
__pycache__/
|
||||
/multiarch/
|
||||
|
||||
.vscode/
|
||||
|
25
Dockerfile
25
Dockerfile
@ -1,12 +1,19 @@
|
||||
FROM python:3-alpine
|
||||
FROM golang:1.16-alpine3.13 AS builder
|
||||
|
||||
COPY requirements.txt /opt/
|
||||
RUN apk --no-cache add gcc musl-dev && \
|
||||
pip install -r /opt/requirements.txt && \
|
||||
apk --no-cache del gcc musl-dev
|
||||
WORKDIR /usr/local/src/docker-net-dhcp
|
||||
COPY go.* ./
|
||||
RUN go mod download
|
||||
|
||||
RUN mkdir -p /opt/plugin /run/docker/plugins /var/run/docker/netns
|
||||
COPY net-dhcp/ /opt/plugin/net_dhcp
|
||||
COPY cmd/ ./cmd/
|
||||
COPY pkg/ ./pkg/
|
||||
RUN mkdir bin/ && go build -o bin/ ./cmd/...
|
||||
|
||||
WORKDIR /opt/plugin
|
||||
ENTRYPOINT ["python", "-m", "net_dhcp"]
|
||||
|
||||
FROM alpine:3.13
|
||||
|
||||
RUN mkdir -p /run/docker/plugins
|
||||
|
||||
COPY --from=builder /usr/local/src/docker-net-dhcp/bin/net-dhcp /usr/sbin/
|
||||
COPY --from=builder /usr/local/src/docker-net-dhcp/bin/udhcpc-handler /usr/lib/net-dhcp/udhcpc-handler
|
||||
|
||||
ENTRYPOINT ["/usr/sbin/net-dhcp"]
|
||||
|
89
Makefile
89
Makefile
@ -1,40 +1,55 @@
|
||||
PLUGIN_NAME = devplayer0/net-dhcp
|
||||
PLUGIN_TAG ?= latest
|
||||
PLUGIN_NAME = ghcr.io/devplayer0/docker-net-dhcp
|
||||
PLUGIN_TAG ?= golang
|
||||
PLATFORMS ?= linux/amd64,linux/arm64
|
||||
|
||||
all: clean build rootfs create enable
|
||||
SOURCES = $(shell find pkg/ cmd/ -name '*.go')
|
||||
BINARY = bin/net-dhcp
|
||||
|
||||
.PHONY: all debug build create enable disable pdebug push clean
|
||||
|
||||
all: create enable
|
||||
|
||||
bin/%: $(SOURCES)
|
||||
go build -o $@ ./cmd/$(shell basename $@)
|
||||
|
||||
debug: $(BINARY)
|
||||
sudo $< -log debug
|
||||
|
||||
build: $(SOURCES)
|
||||
docker build -t $(PLUGIN_NAME):rootfs .
|
||||
|
||||
plugin/rootfs: build
|
||||
mkdir -p plugin/rootfs
|
||||
docker create --name tmp $(PLUGIN_NAME):rootfs
|
||||
docker export tmp | tar xC plugin/rootfs
|
||||
docker rm -vf tmp
|
||||
|
||||
plugin: plugin/rootfs config.json
|
||||
cp config.json $@/
|
||||
|
||||
create: plugin
|
||||
docker plugin rm -f $(PLUGIN_NAME):$(PLUGIN_TAG) || true
|
||||
docker plugin create $(PLUGIN_NAME):$(PLUGIN_TAG) $<
|
||||
docker plugin set $(PLUGIN_NAME):$(PLUGIN_TAG) LOG_LEVEL=trace
|
||||
|
||||
enable: plugin
|
||||
docker plugin enable $(PLUGIN_NAME):$(PLUGIN_TAG)
|
||||
disable:
|
||||
docker plugin disable $(PLUGIN_NAME):$(PLUGIN_TAG)
|
||||
|
||||
pdebug: create enable
|
||||
sudo sh -c 'tail -f /var/lib/docker/plugins/*/rootfs/var/log/net-dhcp.log'
|
||||
|
||||
push: create
|
||||
docker plugin push $(PLUGIN_NAME):$(PLUGIN_TAG)
|
||||
|
||||
multiarch: $(SOURCES)
|
||||
docker buildx build --platform=$(PLATFORMS) -o type=local,dest=$@ .
|
||||
|
||||
push-multiarch: multiarch config.json
|
||||
scripts/push_multiarch_plugin.py -p $(PLATFORMS) config.json multiarch $(PLUGIN_NAME):$(PLUGIN_TAG)
|
||||
|
||||
clean:
|
||||
@echo "### rm ./plugin"
|
||||
@rm -rf ./plugin
|
||||
|
||||
build:
|
||||
@echo "### docker build: rootfs image with net-dhcp"
|
||||
@docker build -t ${PLUGIN_NAME}:rootfs .
|
||||
|
||||
rootfs:
|
||||
@echo "### create rootfs directory in ./plugin/rootfs"
|
||||
@mkdir -p ./plugin/rootfs
|
||||
@docker create --name tmp ${PLUGIN_NAME}:rootfs
|
||||
@docker export tmp | tar -x -C ./plugin/rootfs
|
||||
@echo "### copy config.json to ./plugin/"
|
||||
@cp config.json ./plugin/
|
||||
@docker rm -vf tmp
|
||||
|
||||
create:
|
||||
@echo "### remove existing plugin ${PLUGIN_NAME}:${PLUGIN_TAG} if exists"
|
||||
@docker plugin rm -f ${PLUGIN_NAME}:${PLUGIN_TAG} || true
|
||||
@echo "### create new plugin ${PLUGIN_NAME}:${PLUGIN_TAG} from ./plugin"
|
||||
@docker plugin create ${PLUGIN_NAME}:${PLUGIN_TAG} ./plugin
|
||||
|
||||
debug:
|
||||
@docker run --rm -ti --cap-add CAP_SYS_ADMIN --network host --volume /run/docker/plugins:/run/docker/plugins \
|
||||
--volume /run/docker.sock:/run/docker.sock --volume /var/run/docker/netns:/var/run/docker/netns \
|
||||
${PLUGIN_NAME}:rootfs
|
||||
|
||||
enable:
|
||||
@echo "### enable plugin ${PLUGIN_NAME}:${PLUGIN_TAG}"
|
||||
@docker plugin enable ${PLUGIN_NAME}:${PLUGIN_TAG}
|
||||
|
||||
push:
|
||||
@echo "### push plugin ${PLUGIN_NAME}:${PLUGIN_TAG}"
|
||||
@docker plugin push ${PLUGIN_NAME}:${PLUGIN_TAG}
|
||||
-rm -rf multiarch/
|
||||
-rm -rf plugin/
|
||||
-rm bin/*
|
||||
|
0
bin/.gitkeep
Normal file
0
bin/.gitkeep
Normal file
65
cmd/net-dhcp/main.go
Normal file
65
cmd/net-dhcp/main.go
Normal file
@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/devplayer0/docker-net-dhcp/pkg/plugin"
|
||||
)
|
||||
|
||||
var (
|
||||
logLevel = flag.String("log", "", "log level")
|
||||
logFile = flag.String("logfile", "", "log file")
|
||||
bindSock = flag.String("sock", "/run/docker/plugins/net-dhcp.sock", "bind unix socket")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *logLevel == "" {
|
||||
if *logLevel = os.Getenv("LOG_LEVEL"); *logLevel == "" {
|
||||
*logLevel = "info"
|
||||
}
|
||||
}
|
||||
|
||||
level, err := log.ParseLevel(*logLevel)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to parse log level")
|
||||
}
|
||||
log.SetLevel(level)
|
||||
|
||||
if *logFile != "" {
|
||||
f, err := os.OpenFile(*logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to open log file for writing")
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
log.StandardLogger().Out = f
|
||||
}
|
||||
|
||||
p, err := plugin.NewPlugin()
|
||||
if err != nil {
|
||||
log.WithError(err).Fatal("Failed to create plugin")
|
||||
}
|
||||
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, unix.SIGINT, unix.SIGTERM)
|
||||
|
||||
go func() {
|
||||
log.Info("Starting server...")
|
||||
if err := p.Listen(*bindSock); err != nil {
|
||||
log.WithError(err).Fatal("Failed to start plugin")
|
||||
}
|
||||
}()
|
||||
|
||||
<-sigs
|
||||
log.Info("Shutting down...")
|
||||
if err := p.Close(); err != nil {
|
||||
log.WithError(err).Fatal("Failed to stop plugin")
|
||||
}
|
||||
}
|
48
cmd/udhcpc-handler/main.go
Normal file
48
cmd/udhcpc-handler/main.go
Normal file
@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/devplayer0/docker-net-dhcp/pkg/udhcpc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
log.Fatalf("Usage: %v <event type>", os.Args[0])
|
||||
return
|
||||
}
|
||||
|
||||
event := udhcpc.Event{
|
||||
Type: os.Args[1],
|
||||
}
|
||||
|
||||
switch event.Type {
|
||||
case "bound", "renew":
|
||||
if v6, ok := os.LookupEnv("ipv6"); ok {
|
||||
// Clean up the IP (udhcpc6 emits a _lot_ of zeros)
|
||||
_, netV6, err := net.ParseCIDR(v6 + "/128")
|
||||
if err != nil {
|
||||
log.WithError(err).Warn("Failed to parse IPv6 address")
|
||||
}
|
||||
|
||||
event.Data.IP = netV6.String()
|
||||
} else {
|
||||
event.Data.IP = os.Getenv("ip") + "/" + os.Getenv("mask")
|
||||
event.Data.Gateway = os.Getenv("router")
|
||||
event.Data.Domain = os.Getenv("domain")
|
||||
}
|
||||
case "deconfig", "leasefail", "nak":
|
||||
default:
|
||||
log.Warnf("Ignoring unknown event type `%v`", event.Type)
|
||||
return
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(os.Stdout).Encode(event); err != nil {
|
||||
log.Fatalf("Failed to encode udhcpc event: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
27
config.json
27
config.json
@ -6,8 +6,18 @@
|
||||
"docker.networkdriver/1.0"
|
||||
]
|
||||
},
|
||||
"entrypoint": [ "python", "-m", "net_dhcp" ],
|
||||
"workdir": "/opt/plugin",
|
||||
"entrypoint": ["/usr/sbin/net-dhcp", "-logfile", "/var/log/net-dhcp.log"],
|
||||
"env": [
|
||||
{
|
||||
"description": "Log level",
|
||||
"name": "LOG_LEVEL",
|
||||
"value": "info",
|
||||
"settable": [
|
||||
"value"
|
||||
]
|
||||
}
|
||||
],
|
||||
"workdir": "/",
|
||||
"network": {
|
||||
"type": "host"
|
||||
},
|
||||
@ -19,15 +29,20 @@
|
||||
"options": [
|
||||
"bind"
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/var/run/docker",
|
||||
"destination": "/run/docker",
|
||||
"type": "bind",
|
||||
"options": [
|
||||
"bind"
|
||||
]
|
||||
}
|
||||
],
|
||||
"pidhost": true,
|
||||
"linux": {
|
||||
"capabilities": [
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_RESOURCE"
|
||||
"CAP_SYS_ADMIN"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
23
go.mod
Normal file
23
go.mod
Normal file
@ -0,0 +1,23 @@
|
||||
module github.com/devplayer0/docker-net-dhcp
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/containerd/containerd v1.5.2 // indirect
|
||||
github.com/docker/docker v20.10.7+incompatible
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
google.golang.org/grpc v1.38.0 // indirect
|
||||
)
|
976
go.sum
Normal file
976
go.sum
Normal file
@ -0,0 +1,976 @@
|
||||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
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/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
|
||||
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
|
||||
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
|
||||
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
||||
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
|
||||
github.com/Microsoft/hcsshim v0.8.16 h1:8/auA4LFIZFTGrqfKhGBSXwM6/4X1fHa/xniyEHu8ac=
|
||||
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
|
||||
github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
|
||||
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
|
||||
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
|
||||
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
|
||||
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
||||
github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
||||
github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
|
||||
github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
|
||||
github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
|
||||
github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
|
||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
|
||||
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
|
||||
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
|
||||
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
|
||||
github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ=
|
||||
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
|
||||
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
|
||||
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
|
||||
github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
|
||||
github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
|
||||
github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
|
||||
github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
|
||||
github.com/containerd/containerd v1.5.2 h1:MG/Bg1pbmMb61j3wHCFWPxESXHieiKr2xG64px/k8zQ=
|
||||
github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
|
||||
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
|
||||
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
|
||||
github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8=
|
||||
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
|
||||
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
|
||||
github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
|
||||
github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
|
||||
github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU=
|
||||
github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
|
||||
github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=
|
||||
github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
|
||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||
github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
|
||||
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
|
||||
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
|
||||
github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
|
||||
github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
|
||||
github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=
|
||||
github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
|
||||
github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
|
||||
github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
|
||||
github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
|
||||
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||
github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=
|
||||
github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
|
||||
github.com/containerd/ttrpc v1.0.2 h1:2/O3oTZN36q2xRolk0a2WWGgh7/Vf/liElg5hFYLX9U=
|
||||
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
|
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
|
||||
github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
|
||||
github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY=
|
||||
github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
|
||||
github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw=
|
||||
github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y=
|
||||
github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
|
||||
github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
|
||||
github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
|
||||
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
|
||||
github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
|
||||
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
|
||||
github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=
|
||||
github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
||||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
|
||||
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/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ=
|
||||
github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
|
||||
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
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/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
|
||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
|
||||
github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI=
|
||||
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
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/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
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/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.13 h1:eSvu8Tmq6j2psUJqJrLcWH6K3w5Dwc+qipbaA6eVEN4=
|
||||
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
|
||||
github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
|
||||
github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
|
||||
github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
|
||||
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
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/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v1.0.0-rc93 h1:x2UMpOOVf3kQ8arv/EsDGwim8PTNqzL1/EYDr/+scOM=
|
||||
github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
|
||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d h1:pNa8metDkwZjb9g4T8s+krQ+HRgZAkqnXml+wNir/+s=
|
||||
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
|
||||
github.com/opencontainers/selinux v1.8.0 h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM=
|
||||
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/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/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
|
||||
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
|
||||
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
|
||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
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/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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-20180905080454-ebe1bf3edb33/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-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b h1:qh4f65QIVFjq9eBURLEYWqaEXmOyqdUyiBSgaXWccWk=
|
||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
|
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
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=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
|
||||
k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
|
||||
k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
|
||||
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||
k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||
k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
|
||||
k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
|
||||
k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
|
||||
k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
|
||||
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
|
||||
k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
|
||||
k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
|
||||
k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
|
||||
k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
|
||||
k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
|
||||
k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=
|
||||
k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
|
||||
k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
|
||||
k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
@ -1,23 +0,0 @@
|
||||
import logging
|
||||
|
||||
from flask import Flask, jsonify
|
||||
|
||||
class NetDhcpError(Exception):
|
||||
def __init__(self, status, *args):
|
||||
Exception.__init__(self, *args)
|
||||
self.status = status
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
from . import network
|
||||
|
||||
logger = logging.getLogger('gunicorn.error')
|
||||
|
||||
@app.errorhandler(404)
|
||||
def err_not_found(_e):
|
||||
return jsonify({'Err': 'API not found'}), 404
|
||||
|
||||
@app.errorhandler(Exception)
|
||||
def err(e):
|
||||
logger.exception(e)
|
||||
return jsonify({'Err': str(e)}), 500
|
@ -1,14 +0,0 @@
|
||||
import logging
|
||||
import socketserver
|
||||
from werkzeug.serving import run_simple
|
||||
from . import app
|
||||
|
||||
fh = logging.FileHandler('/var/log/net-dhcp.log')
|
||||
fh.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(message)s'))
|
||||
|
||||
logger = logging.getLogger('net-dhcp')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.addHandler(fh)
|
||||
|
||||
socketserver.TCPServer.allow_reuse_address = True
|
||||
run_simple('unix:///run/docker/plugins/net-dhcp.sock', 0, app)
|
@ -1,394 +0,0 @@
|
||||
import itertools
|
||||
import ipaddress
|
||||
import logging
|
||||
import atexit
|
||||
import socket
|
||||
import time
|
||||
import threading
|
||||
import subprocess
|
||||
|
||||
import pyroute2
|
||||
from pyroute2.netlink.rtnl import rtypes
|
||||
import docker
|
||||
from flask import request, jsonify
|
||||
|
||||
from . import NetDhcpError, udhcpc, app
|
||||
|
||||
OPTS_KEY = 'com.docker.network.generic'
|
||||
OPT_BRIDGE = 'bridge'
|
||||
OPT_IPV6 = 'ipv6'
|
||||
|
||||
logger = logging.getLogger('net-dhcp')
|
||||
|
||||
ndb = pyroute2.NDB()
|
||||
@atexit.register
|
||||
def close_ndb():
|
||||
ndb.close()
|
||||
|
||||
client = docker.from_env()
|
||||
@atexit.register
|
||||
def close_docker():
|
||||
client.close()
|
||||
|
||||
gateway_hints = {}
|
||||
container_dhcp_clients = {}
|
||||
@atexit.register
|
||||
def cleanup_dhcp():
|
||||
for endpoint, dhcp in container_dhcp_clients.items():
|
||||
logger.warning('cleaning up orphaned container DHCP client (endpoint "%s")', endpoint)
|
||||
dhcp.stop()
|
||||
|
||||
def veth_pair(e):
|
||||
return f'dh-{e[:12]}', f'{e[:12]}-dh'
|
||||
|
||||
def iface_addrs(iface):
|
||||
return list(map(lambda a: ipaddress.ip_interface((a['address'], a['prefixlen'])), iface.ipaddr))
|
||||
def iface_nets(iface):
|
||||
return list(map(lambda n: n.network, iface_addrs(iface)))
|
||||
|
||||
def get_bridges():
|
||||
reserved_nets = set(map(ipaddress.ip_network, map(lambda c: c['Subnet'], \
|
||||
itertools.chain.from_iterable(map(lambda i: i['Config'], filter(lambda i: i['Driver'] != 'net-dhcp', \
|
||||
map(lambda n: n.attrs['IPAM'], client.networks.list())))))))
|
||||
|
||||
return dict(map(lambda i: (i['ifname'], i), filter(lambda i: i['kind'] == 'bridge' and not \
|
||||
set(iface_nets(i)).intersection(reserved_nets), map(lambda i: ndb.interfaces[i.ifname], ndb.interfaces))))
|
||||
|
||||
def net_bridge(n):
|
||||
return ndb.interfaces[client.networks.get(n).attrs['Options'][OPT_BRIDGE]]
|
||||
def ipv6_enabled(n):
|
||||
options = client.networks.get(n).attrs['Options']
|
||||
return OPT_IPV6 in options and options[OPT_IPV6] == 'true'
|
||||
|
||||
def endpoint_container_iface(n, e):
|
||||
for cid, info in client.networks.get(n).attrs['Containers'].items():
|
||||
if info['EndpointID'] == e:
|
||||
container = client.containers.get(cid)
|
||||
netns = f'/proc/{container.attrs["State"]["Pid"]}/ns/net'
|
||||
|
||||
with pyroute2.NetNS(netns) as rtnl:
|
||||
for link in rtnl.get_links():
|
||||
attrs = dict(link['attrs'])
|
||||
if attrs['IFLA_ADDRESS'] == info['MacAddress']:
|
||||
return {
|
||||
'netns': netns,
|
||||
'ifname': attrs['IFLA_IFNAME'],
|
||||
'address': attrs['IFLA_ADDRESS']
|
||||
}
|
||||
break
|
||||
return None
|
||||
def await_endpoint_container_iface(n, e, timeout=5):
|
||||
start = time.time()
|
||||
iface = None
|
||||
while time.time() - start < timeout:
|
||||
try:
|
||||
iface = endpoint_container_iface(n, e)
|
||||
except docker.errors.NotFound:
|
||||
time.sleep(0.5)
|
||||
if not iface:
|
||||
raise NetDhcpError('Timed out waiting for container to become availabile')
|
||||
return iface
|
||||
|
||||
def endpoint_container_hostname(n, e):
|
||||
for cid, info in client.networks.get(n).attrs['Containers'].items():
|
||||
if info['EndpointID'] == e:
|
||||
return client.containers.get(cid).attrs['Config']['Hostname']
|
||||
return None
|
||||
|
||||
@app.route('/NetworkDriver.GetCapabilities', methods=['POST'])
|
||||
def net_get_capabilities():
|
||||
return jsonify({
|
||||
'Scope': 'local',
|
||||
'ConnectivityScope': 'global'
|
||||
})
|
||||
|
||||
@app.route('/NetworkDriver.CreateNetwork', methods=['POST'])
|
||||
def create_net():
|
||||
req = request.get_json(force=True)
|
||||
for data in req['IPv4Data']:
|
||||
if data['AddressSpace'] != 'null' or data['Pool'] != '0.0.0.0/0':
|
||||
return jsonify({'Err': 'Only the null IPAM driver is supported'}), 400
|
||||
|
||||
options = req['Options'][OPTS_KEY]
|
||||
if OPT_BRIDGE not in options:
|
||||
return jsonify({'Err': 'No bridge provided'}), 400
|
||||
# We have to use a custom "enable IPv6" option because Docker's null IPAM driver doesn't support IPv6 and a plugin
|
||||
# IPAM driver isn't allowed to return an empty address
|
||||
if OPT_IPV6 in options and options[OPT_IPV6] not in ('', 'true', 'false'):
|
||||
return jsonify({'Err': 'Invalid boolean value for ipv6'}), 400
|
||||
|
||||
desired = options[OPT_BRIDGE]
|
||||
bridges = get_bridges()
|
||||
if desired not in bridges:
|
||||
return jsonify({'Err': f'Bridge "{desired}" not found (or the specified bridge is already used by Docker)'}), 400
|
||||
|
||||
logger.info('Creating network "%s" (using bridge "%s")', req['NetworkID'], desired)
|
||||
return jsonify({})
|
||||
|
||||
@app.route('/NetworkDriver.DeleteNetwork', methods=['POST'])
|
||||
def delete_net():
|
||||
return jsonify({})
|
||||
|
||||
@app.route('/NetworkDriver.CreateEndpoint', methods=['POST'])
|
||||
def create_endpoint():
|
||||
req = request.get_json(force=True)
|
||||
network_id = req['NetworkID']
|
||||
endpoint_id = req['EndpointID']
|
||||
req_iface = req['Interface']
|
||||
|
||||
bridge = net_bridge(network_id)
|
||||
bridge_addrs = iface_addrs(bridge)
|
||||
|
||||
if_host, if_container = veth_pair(endpoint_id)
|
||||
logger.info('creating veth pair %s <=> %s', if_host, if_container)
|
||||
if_host = (ndb.interfaces.create(ifname=if_host, kind='veth', peer=if_container)
|
||||
.set('state', 'up')
|
||||
.commit())
|
||||
|
||||
try:
|
||||
start = time.time()
|
||||
while isinstance(if_container, str) and time.time() - start < 10:
|
||||
try:
|
||||
if_container = (ndb.interfaces[if_container]
|
||||
.set('state', 'up')
|
||||
.commit())
|
||||
except KeyError:
|
||||
time.sleep(0.5)
|
||||
if isinstance(if_container, str):
|
||||
raise NetDhcpError(f'timed out waiting for {if_container} to appear in host')
|
||||
|
||||
(bridge
|
||||
.add_port(if_host)
|
||||
.commit())
|
||||
|
||||
res_iface = {
|
||||
'MacAddress': '',
|
||||
'Address': '',
|
||||
'AddressIPv6': ''
|
||||
}
|
||||
|
||||
if 'MacAddress' in req_iface and req_iface['MacAddress']:
|
||||
(if_container
|
||||
.set('address', req_iface['MacAddress'])
|
||||
.commit())
|
||||
else:
|
||||
res_iface['MacAddress'] = if_container['address']
|
||||
|
||||
def try_addr(type_):
|
||||
addr = None
|
||||
k = 'AddressIPv6' if type_ == 'v6' else 'Address'
|
||||
if k in req_iface and req_iface[k]:
|
||||
# TODO: Should we allow static IP's somehow?
|
||||
# Just validate the address, Docker will add it to the interface for us
|
||||
#addr = ipaddress.ip_interface(req_iface[k])
|
||||
#for bridge_addr in bridge_addrs:
|
||||
# if addr.ip == bridge_addr.ip:
|
||||
# raise NetDhcpError(400, f'Address {addr} is already in use on bridge {bridge["ifname"]}')
|
||||
raise NetDhcpError('Only the null IPAM driver is supported')
|
||||
else:
|
||||
dhcp = udhcpc.DHCPClient(if_container, v6=type_ == 'v6', once=True)
|
||||
addr = dhcp.finish()
|
||||
if not addr:
|
||||
return
|
||||
res_iface[k] = str(addr)
|
||||
|
||||
if dhcp.gateway:
|
||||
gateway_hints[endpoint_id] = dhcp.gateway
|
||||
logger.info('Adding IP%s address %s to %s', type_, addr, if_container['ifname'])
|
||||
|
||||
try_addr('v4')
|
||||
if ipv6_enabled(network_id):
|
||||
try_addr('v6')
|
||||
|
||||
res = jsonify({
|
||||
'Interface': res_iface
|
||||
})
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
|
||||
if not isinstance(if_container, str):
|
||||
(bridge
|
||||
.del_port(if_host)
|
||||
.commit())
|
||||
(if_host
|
||||
.remove()
|
||||
.commit())
|
||||
|
||||
if isinstance(e, NetDhcpError):
|
||||
res = jsonify({'Err': str(e)}), e.status
|
||||
else:
|
||||
res = jsonify({'Err': str(e)}), 500
|
||||
finally:
|
||||
return res
|
||||
|
||||
@app.route('/NetworkDriver.EndpointOperInfo', methods=['POST'])
|
||||
def endpoint_info():
|
||||
req = request.get_json(force=True)
|
||||
|
||||
bridge = net_bridge(req['NetworkID'])
|
||||
if_host, _if_container = veth_pair(req['EndpointID'])
|
||||
if_host = ndb.interfaces[if_host]
|
||||
|
||||
return jsonify({
|
||||
'bridge': bridge['ifname'],
|
||||
'if_host': {
|
||||
'name': if_host['ifname'],
|
||||
'mac': if_host['address']
|
||||
}
|
||||
})
|
||||
|
||||
@app.route('/NetworkDriver.DeleteEndpoint', methods=['POST'])
|
||||
def delete_endpoint():
|
||||
req = request.get_json(force=True)
|
||||
|
||||
bridge = net_bridge(req['NetworkID'])
|
||||
if_host, _if_container = veth_pair(req['EndpointID'])
|
||||
if_host = ndb.interfaces[if_host]
|
||||
|
||||
bridge.del_port(if_host['ifname'])
|
||||
(if_host
|
||||
.remove()
|
||||
.commit())
|
||||
|
||||
return jsonify({})
|
||||
|
||||
@app.route('/NetworkDriver.Join', methods=['POST'])
|
||||
def join():
|
||||
req = request.get_json(force=True)
|
||||
network = req['NetworkID']
|
||||
endpoint = req['EndpointID']
|
||||
|
||||
bridge = net_bridge(req['NetworkID'])
|
||||
_if_host, if_container = veth_pair(req['EndpointID'])
|
||||
|
||||
res = {
|
||||
'InterfaceName': {
|
||||
'SrcName': if_container,
|
||||
'DstPrefix': bridge['ifname']
|
||||
},
|
||||
'StaticRoutes': []
|
||||
}
|
||||
|
||||
if endpoint in gateway_hints:
|
||||
gateway = gateway_hints[endpoint]
|
||||
logger.info('Setting IPv4 gateway from DHCP (%s)', gateway)
|
||||
res['Gateway'] = str(gateway)
|
||||
del gateway_hints[endpoint]
|
||||
|
||||
ipv6 = ipv6_enabled(network)
|
||||
for route in bridge.routes:
|
||||
if route['type'] != rtypes['RTN_UNICAST'] or \
|
||||
(route['family'] == socket.AF_INET6 and not ipv6):
|
||||
continue
|
||||
|
||||
if route['dst'] in ('', '/0'):
|
||||
if route['family'] == socket.AF_INET and 'Gateway' not in res:
|
||||
logger.info('Adding IPv4 gateway %s', route['gateway'])
|
||||
res['Gateway'] = route['gateway']
|
||||
elif route['family'] == socket.AF_INET6 and 'GatewayIPv6' not in res:
|
||||
logger.info('Adding IPv6 gateway %s', route['gateway'])
|
||||
res['GatewayIPv6'] = route['gateway']
|
||||
elif route['gateway']:
|
||||
dst = f'{route["dst"]}/{route["dst_len"]}'
|
||||
logger.info('Adding route to %s via %s', dst, route['gateway'])
|
||||
res['StaticRoutes'].append({
|
||||
'Destination': dst,
|
||||
'RouteType': 0,
|
||||
'NextHop': route['gateway']
|
||||
})
|
||||
|
||||
container_dhcp_clients[endpoint] = ContainerDHCPManager(network, endpoint)
|
||||
return jsonify(res)
|
||||
|
||||
@app.route('/NetworkDriver.Leave', methods=['POST'])
|
||||
def leave():
|
||||
req = request.get_json(force=True)
|
||||
endpoint = req['EndpointID']
|
||||
|
||||
if endpoint in container_dhcp_clients:
|
||||
container_dhcp_clients[endpoint].stop()
|
||||
del container_dhcp_clients[endpoint]
|
||||
|
||||
return jsonify({})
|
||||
|
||||
# Trying to grab the container's attributes (to get the network namespace)
|
||||
# will deadlock (since Docker is waiting on us), so we must defer starting
|
||||
# the DHCP client
|
||||
class ContainerDHCPManager:
|
||||
def __init__(self, network, endpoint):
|
||||
self.network = network
|
||||
self.endpoint = endpoint
|
||||
self.ipv6 = ipv6_enabled(network)
|
||||
|
||||
self.dhcp = None
|
||||
self.dhcp6 = None
|
||||
self._thread = threading.Thread(target=self.run)
|
||||
self._thread.start()
|
||||
|
||||
def _on_event(self, dhcp, event_type, _event):
|
||||
if event_type != udhcpc.EventType.RENEW or not dhcp.gateway:
|
||||
return
|
||||
|
||||
logger.info('[dhcp container] Replacing gateway with %s', dhcp.gateway)
|
||||
subprocess.check_call(['nsenter', f'-n{dhcp.netns}', '--', '/sbin/ip', 'route', 'replace', 'default', 'via',
|
||||
str(dhcp.gateway)], timeout=1, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
|
||||
# TODO: Adding default route with NDB seems to be broken (because of the dst syntax?)
|
||||
#for route in ndb.routes:
|
||||
# if route['type'] != rtypes['RTN_UNICAST'] or \
|
||||
# route['oif'] != dhcp.iface['index'] or \
|
||||
# (route['family'] == socket.AF_INET6 and not self.ipv6) or \
|
||||
# route['dst'] not in ('', '/0'):
|
||||
# continue
|
||||
|
||||
# # Needed because Route.remove() doesn't like a blank destination
|
||||
# logger.info('Removing default route via %s', route['gateway'])
|
||||
# route['dst'] = '::' if route['family'] == socket.AF_INET6 else '0.0.0.0'
|
||||
# (route
|
||||
# .remove()
|
||||
# .commit())
|
||||
|
||||
#logger.info('Adding default route via %s', dhcp.gateway)
|
||||
#(ndb.routes.add({'oif': dhcp.iface['index'], 'gateway': dhcp.gateway})
|
||||
# .commit())
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
iface = await_endpoint_container_iface(self.network, self.endpoint)
|
||||
hostname = endpoint_container_hostname(self.network, self.endpoint)
|
||||
|
||||
self.dhcp = udhcpc.DHCPClient(iface, event_listener=self._on_event, hostname=hostname)
|
||||
logger.info('Starting DHCPv4 client on %s in container namespace %s', iface['ifname'], \
|
||||
self.dhcp.netns)
|
||||
|
||||
if self.ipv6:
|
||||
self.dhcp6 = udhcpc.DHCPClient(iface, v6=True, event_listener=self._on_event, hostname=hostname)
|
||||
logger.info('Starting DHCPv6 client on %s in container namespace %s', iface['ifname'], \
|
||||
self.dhcp6.netns)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
if self.dhcp:
|
||||
self.dhcp.finish(timeout=1)
|
||||
|
||||
def stop(self):
|
||||
if not self.dhcp:
|
||||
return
|
||||
|
||||
try:
|
||||
logger.info('Shutting down DHCPv4 client on %s in container namespace %s', \
|
||||
self.dhcp.iface['ifname'], self.dhcp.netns)
|
||||
self.dhcp.finish(timeout=1)
|
||||
finally:
|
||||
try:
|
||||
if self.ipv6:
|
||||
logger.info('Shutting down DHCPv6 client on %s in container namespace %s', \
|
||||
self.dhcp6.iface['ifname'], self.dhcp.netns)
|
||||
self.dhcp6.finish(timeout=1)
|
||||
finally:
|
||||
self._thread.join()
|
||||
|
||||
# we have to do this since the docker client leaks sockets...
|
||||
global client
|
||||
client.close()
|
||||
client = docker.from_env()
|
@ -1,148 +0,0 @@
|
||||
from enum import Enum
|
||||
import ipaddress
|
||||
import json
|
||||
import struct
|
||||
import binascii
|
||||
import os
|
||||
from os import path
|
||||
from select import select
|
||||
import threading
|
||||
import subprocess
|
||||
import logging
|
||||
|
||||
from eventfd import EventFD
|
||||
import posix_ipc
|
||||
|
||||
HANDLER_SCRIPT = path.join(path.dirname(__file__), 'udhcpc_handler.py')
|
||||
AWAIT_INTERVAL = 0.1
|
||||
VENDOR_ID = 'docker'
|
||||
|
||||
class EventType(Enum):
|
||||
BOUND = 'bound'
|
||||
RENEW = 'renew'
|
||||
DECONFIG = 'deconfig'
|
||||
LEASEFAIL = 'leasefail'
|
||||
|
||||
logger = logging.getLogger('net-dhcp')
|
||||
|
||||
class DHCPClientError(Exception):
|
||||
pass
|
||||
|
||||
def _nspopen_wrapper(netns):
|
||||
return lambda cmd, *args, **kwargs: subprocess.Popen(['nsenter', f'-n{netns}', '--'] + cmd, *args, **kwargs)
|
||||
class DHCPClient:
|
||||
def __init__(self, iface, v6=False, once=False, hostname=None, event_listener=None):
|
||||
self.iface = iface
|
||||
self.v6 = v6
|
||||
self.once = once
|
||||
self.event_listeners = [DHCPClient._attr_listener]
|
||||
if event_listener:
|
||||
self.event_listeners.append(event_listener)
|
||||
|
||||
self.netns = None
|
||||
if 'netns' in iface:
|
||||
self.netns = iface['netns']
|
||||
logger.debug('udhcpc using netns %s', self.netns)
|
||||
|
||||
Popen = _nspopen_wrapper(self.netns) if self.netns else subprocess.Popen
|
||||
bin_path = '/usr/bin/udhcpc6' if v6 else '/sbin/udhcpc'
|
||||
cmdline = [bin_path, '-s', HANDLER_SCRIPT, '-i', iface['ifname'], '-f']
|
||||
cmdline.append('-q' if once else '-R')
|
||||
if hostname:
|
||||
cmdline.append('-x')
|
||||
if v6:
|
||||
# TODO: We encode the fqdn for DHCPv6 because udhcpc6 seems to be broken
|
||||
# flags: S bit set (see RFC4704)
|
||||
enc_hostname = hostname.encode('utf-8')
|
||||
enc_hostname = struct.pack('BB', 0b0001, len(enc_hostname)) + enc_hostname
|
||||
enc_hostname = binascii.hexlify(enc_hostname).decode('ascii')
|
||||
hostname_opt = f'0x27:{enc_hostname}'
|
||||
else:
|
||||
hostname_opt = f'hostname:{hostname}'
|
||||
cmdline.append(hostname_opt)
|
||||
if not v6:
|
||||
cmdline += ['-V', VENDOR_ID]
|
||||
|
||||
self._suffix = '6' if v6 else ''
|
||||
self._event_queue = posix_ipc.MessageQueue(f'/udhcpc{self._suffix}_{iface["address"].replace(":", "_")}', \
|
||||
flags=os.O_CREAT | os.O_EXCL, max_messages=2, max_message_size=1024)
|
||||
self.proc = Popen(cmdline, env={'EVENT_QUEUE': self._event_queue.name}, stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
if hostname:
|
||||
logger.debug('[udhcpc%s#%d] using hostname "%s"', self._suffix, self.proc.pid, hostname)
|
||||
|
||||
self._has_lease = threading.Event()
|
||||
self.ip = None
|
||||
self.gateway = None
|
||||
self.domain = None
|
||||
|
||||
self._shutdown_event = EventFD()
|
||||
self.shutdown = False
|
||||
self._event_thread = threading.Thread(target=self._read_events)
|
||||
self._event_thread.start()
|
||||
|
||||
def _attr_listener(self, event_type, event):
|
||||
if event_type in (EventType.BOUND, EventType.RENEW):
|
||||
self.ip = ipaddress.ip_interface(event['ip'])
|
||||
if 'gateway' in event:
|
||||
self.gateway = ipaddress.ip_address(event['gateway'])
|
||||
else:
|
||||
self.gateway = None
|
||||
self.domain = event.get('domain')
|
||||
self._has_lease.set()
|
||||
elif event_type == EventType.DECONFIG:
|
||||
self._has_lease.clear()
|
||||
self.ip = None
|
||||
self.gateway = None
|
||||
self.domain = None
|
||||
|
||||
def _read_events(self):
|
||||
while True:
|
||||
r, _w, _e = select([self._shutdown_event, self._event_queue.mqd], [], [])
|
||||
if self._shutdown_event in r:
|
||||
break
|
||||
|
||||
msg, _priority = self._event_queue.receive()
|
||||
event = json.loads(msg.decode('utf-8'))
|
||||
try:
|
||||
event['type'] = EventType(event['type'])
|
||||
except ValueError:
|
||||
logger.warning('udhcpc%s#%d unknown event "%s"', self._suffix, self.proc.pid, event)
|
||||
continue
|
||||
|
||||
logger.debug('[udhcp%s#%d event] %s', self._suffix, self.proc.pid, event)
|
||||
for listener in self.event_listeners:
|
||||
try:
|
||||
listener(self, event['type'], event)
|
||||
except Exception as ex:
|
||||
logger.exception(ex)
|
||||
self.shutdown = True
|
||||
del self._shutdown_event
|
||||
|
||||
def await_ip(self, timeout=10):
|
||||
if not self._has_lease.wait(timeout=timeout):
|
||||
raise DHCPClientError(f'Timed out waiting for lease from udhcpc{self._suffix}')
|
||||
|
||||
return self.ip
|
||||
|
||||
def finish(self, timeout=5):
|
||||
if self.shutdown or self._shutdown_event.is_set():
|
||||
return
|
||||
|
||||
try:
|
||||
if self.proc.returncode is not None and (not self.once or self.proc.returncode != 0):
|
||||
raise DHCPClientError(f'udhcpc{self._suffix} exited early with code {self.proc.returncode}')
|
||||
if self.once:
|
||||
self.await_ip()
|
||||
else:
|
||||
self.proc.terminate()
|
||||
|
||||
if self.proc.wait(timeout=timeout) != 0:
|
||||
raise DHCPClientError(f'udhcpc{self._suffix} exited with non-zero exit code {self.proc.returncode}')
|
||||
|
||||
return self.ip
|
||||
finally:
|
||||
self._shutdown_event.set()
|
||||
self._event_thread.join()
|
||||
self._event_queue.close()
|
||||
self._event_queue.unlink()
|
@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import json
|
||||
import sys
|
||||
from os import environ as env
|
||||
|
||||
import posix_ipc
|
||||
|
||||
if __name__ != '__main__':
|
||||
print('You shouldn\'t be importing this script!')
|
||||
sys.exit(1)
|
||||
|
||||
event = {'type': sys.argv[1]}
|
||||
if event['type'] in ('bound', 'renew'):
|
||||
if 'ipv6' in env:
|
||||
event['ip'] = env['ipv6']
|
||||
else:
|
||||
event['ip'] = f'{env["ip"]}/{env["mask"]}'
|
||||
if 'router' in env:
|
||||
event['gateway'] = env['router']
|
||||
if 'domain' in env:
|
||||
event['domain'] = env['domain']
|
||||
elif event['type'] in ('deconfig', 'leasefail', 'nak'):
|
||||
pass
|
||||
else:
|
||||
event['type'] = 'unknown'
|
||||
|
||||
queue = posix_ipc.MessageQueue(env['EVENT_QUEUE'])
|
||||
queue.send(json.dumps(event))
|
||||
queue.close()
|
300
pkg/plugin/dhcp_manager.go
Normal file
300
pkg/plugin/dhcp_manager.go
Normal file
@ -0,0 +1,300 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
dTypes "github.com/docker/docker/api/types"
|
||||
docker "github.com/docker/docker/client"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/devplayer0/docker-net-dhcp/pkg/udhcpc"
|
||||
"github.com/devplayer0/docker-net-dhcp/pkg/util"
|
||||
)
|
||||
|
||||
const pollTime = 100 * time.Millisecond
|
||||
|
||||
type dhcpManager struct {
|
||||
docker *docker.Client
|
||||
joinReq JoinRequest
|
||||
opts DHCPNetworkOptions
|
||||
|
||||
LastIP *netlink.Addr
|
||||
LastIPv6 *netlink.Addr
|
||||
|
||||
hostname string
|
||||
nsHandle netns.NsHandle
|
||||
netHandle *netlink.Handle
|
||||
ctrLink netlink.Link
|
||||
|
||||
stopChan chan struct{}
|
||||
errChan chan error
|
||||
errChanV6 chan error
|
||||
}
|
||||
|
||||
func newDHCPManager(docker *docker.Client, r JoinRequest, opts DHCPNetworkOptions) *dhcpManager {
|
||||
return &dhcpManager{
|
||||
docker: docker,
|
||||
joinReq: r,
|
||||
opts: opts,
|
||||
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *dhcpManager) logFields(v6 bool) log.Fields {
|
||||
return log.Fields{
|
||||
"network": m.joinReq.NetworkID[:12],
|
||||
"endpoint": m.joinReq.EndpointID[:12],
|
||||
"sandbox": m.joinReq.SandboxKey,
|
||||
"is_ipv6": v6,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *dhcpManager) renew(v6 bool, info udhcpc.Info) error {
|
||||
lastIP := m.LastIP
|
||||
if v6 {
|
||||
lastIP = m.LastIPv6
|
||||
}
|
||||
|
||||
ip, err := netlink.ParseAddr(info.IP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse IP address: %w", err)
|
||||
}
|
||||
|
||||
if !ip.Equal(*lastIP) {
|
||||
// TODO: We can't deal with a different renewed IP for the same reason as described for `bound`
|
||||
log.
|
||||
WithFields(m.logFields(v6)).
|
||||
WithField("old_ip", lastIP).
|
||||
WithField("new_ip", ip).
|
||||
Warn("udhcpc renew with changed IP")
|
||||
}
|
||||
|
||||
if !v6 && info.Gateway != "" {
|
||||
newGateway := net.ParseIP(info.Gateway)
|
||||
|
||||
routes, err := m.netHandle.RouteListFiltered(unix.AF_INET, &netlink.Route{
|
||||
LinkIndex: m.ctrLink.Attrs().Index,
|
||||
Dst: nil,
|
||||
}, netlink.RT_FILTER_OIF|netlink.RT_FILTER_DST)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list routes: %w", err)
|
||||
}
|
||||
|
||||
if len(routes) == 0 {
|
||||
log.
|
||||
WithFields(m.logFields(v6)).
|
||||
WithField("gateway", newGateway).
|
||||
Info("udhcpc renew adding default route")
|
||||
|
||||
if err := m.netHandle.RouteAdd(&netlink.Route{
|
||||
LinkIndex: m.ctrLink.Attrs().Index,
|
||||
Gw: newGateway,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to add default route: %w", err)
|
||||
}
|
||||
} else if !newGateway.Equal(routes[0].Gw) {
|
||||
log.
|
||||
WithFields(m.logFields(v6)).
|
||||
WithField("old_gateway", routes[0].Gw).
|
||||
WithField("new_gateway", newGateway).
|
||||
Info("udhcpc renew replacing default route")
|
||||
|
||||
routes[0].Gw = newGateway
|
||||
if err := m.netHandle.RouteReplace(&routes[0]); err != nil {
|
||||
return fmt.Errorf("failed to replace default route: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *dhcpManager) setupClient(v6 bool) (chan error, error) {
|
||||
v6Str := ""
|
||||
if v6 {
|
||||
v6Str = "v6"
|
||||
}
|
||||
|
||||
client, err := udhcpc.NewDHCPClient(m.ctrLink.Attrs().Name, &udhcpc.DHCPClientOptions{
|
||||
Hostname: m.hostname,
|
||||
V6: v6,
|
||||
Namespace: m.joinReq.SandboxKey,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create DHCP%v client: %w", v6Str, err)
|
||||
}
|
||||
|
||||
events, err := client.Start()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to start DHCP%v client: %w", v6Str, err)
|
||||
}
|
||||
|
||||
errChan := make(chan error)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-events:
|
||||
switch event.Type {
|
||||
// TODO: We can't really allow the IP in the container to be deleted, it'll delete some of our previously
|
||||
// copied routes. Should this be handled somehow?
|
||||
//case "deconfig":
|
||||
// ip := m.LastIP
|
||||
// if v6 {
|
||||
// ip = m.LastIPv6
|
||||
// }
|
||||
|
||||
// log.
|
||||
// WithFields(m.logFields(v6)).
|
||||
// WithField("ip", ip).
|
||||
// Info("udhcpc deconfiguring, deleting previously acquired IP")
|
||||
// if err := m.netHandle.AddrDel(m.ctrLink, ip); err != nil {
|
||||
// log.
|
||||
// WithError(err).
|
||||
// WithFields(m.logFields(v6)).
|
||||
// WithField("ip", ip).
|
||||
// Error("Failed to delete existing udhcpc address")
|
||||
// }
|
||||
// We're `bound` from the beginning
|
||||
//case "bound":
|
||||
case "renew":
|
||||
log.
|
||||
WithFields(m.logFields(v6)).
|
||||
Debug("udhcpc renew")
|
||||
|
||||
if err := m.renew(v6, event.Data); err != nil {
|
||||
log.
|
||||
WithError(err).
|
||||
WithFields(m.logFields(v6)).
|
||||
WithField("gateway", event.Data.Gateway).
|
||||
WithField("new_ip", event.Data.IP).
|
||||
Error("Failed to execute IP renewal")
|
||||
}
|
||||
case "leasefail":
|
||||
log.WithFields(m.logFields(v6)).Warn("udhcpc failed to get a lease")
|
||||
case "nak":
|
||||
log.WithFields(m.logFields(v6)).Warn("udhcpc client received NAK")
|
||||
}
|
||||
|
||||
case <-m.stopChan:
|
||||
log.WithFields(m.logFields(v6)).Info("Shutting down persistent DHCP client")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
errChan <- client.Finish(ctx)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return errChan, nil
|
||||
}
|
||||
|
||||
func (m *dhcpManager) Start(ctx context.Context) error {
|
||||
var err error
|
||||
m.nsHandle, err = util.AwaitNetNS(ctx, m.joinReq.SandboxKey, pollTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get sandbox network namespace: %w", err)
|
||||
}
|
||||
|
||||
m.netHandle, err = netlink.NewHandleAt(m.nsHandle)
|
||||
if err != nil {
|
||||
m.nsHandle.Close()
|
||||
return fmt.Errorf("failed to open netlink handle in sandbox namespace: %w", err)
|
||||
}
|
||||
|
||||
if err := func() error {
|
||||
hostName, oldCtrName := vethPairNames(m.joinReq.EndpointID)
|
||||
hostLink, err := netlink.LinkByName(hostName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find host side of veth pair: %w", err)
|
||||
}
|
||||
hostVeth, ok := hostLink.(*netlink.Veth)
|
||||
if !ok {
|
||||
return util.ErrNotVEth
|
||||
}
|
||||
|
||||
ctrIndex, err := netlink.VethPeerIndex(hostVeth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get container side of veth's index: %w", err)
|
||||
}
|
||||
|
||||
if err := util.AwaitCondition(ctx, func() (bool, error) {
|
||||
m.ctrLink, err = util.AwaitLinkByIndex(ctx, m.netHandle, ctrIndex, pollTime)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get link for container side of veth pair: %w", err)
|
||||
}
|
||||
|
||||
return m.ctrLink.Attrs().Name != oldCtrName, nil
|
||||
}, pollTime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dockerNet, err := m.docker.NetworkInspect(ctx, m.joinReq.NetworkID, dTypes.NetworkInspectOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get Docker network info: %w", err)
|
||||
}
|
||||
|
||||
var ctrID string
|
||||
for id, info := range dockerNet.Containers {
|
||||
if info.EndpointID == m.joinReq.EndpointID {
|
||||
ctrID = id
|
||||
break
|
||||
}
|
||||
}
|
||||
if ctrID == "" {
|
||||
return util.ErrNoContainer
|
||||
}
|
||||
|
||||
ctr, err := m.docker.ContainerInspect(ctx, ctrID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get Docker container info: %w", err)
|
||||
}
|
||||
m.hostname = ctr.Config.Hostname
|
||||
|
||||
if m.errChan, err = m.setupClient(false); err != nil {
|
||||
close(m.stopChan)
|
||||
return err
|
||||
}
|
||||
|
||||
if m.opts.IPv6 {
|
||||
if m.errChanV6, err = m.setupClient(true); err != nil {
|
||||
close(m.stopChan)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}(); err != nil {
|
||||
m.netHandle.Delete()
|
||||
m.nsHandle.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *dhcpManager) Stop() error {
|
||||
defer m.nsHandle.Close()
|
||||
defer m.netHandle.Delete()
|
||||
|
||||
close(m.stopChan)
|
||||
|
||||
if err := <-m.errChan; err != nil {
|
||||
return fmt.Errorf("failed shut down DHCP client: %w", err)
|
||||
}
|
||||
if m.opts.IPv6 {
|
||||
if err := <-m.errChanV6; err != nil {
|
||||
return fmt.Errorf("failed shut down DHCPv6 client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
218
pkg/plugin/endpoints.go
Normal file
218
pkg/plugin/endpoints.go
Normal file
@ -0,0 +1,218 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/devplayer0/docker-net-dhcp/pkg/util"
|
||||
)
|
||||
|
||||
// Payloads are based on https://github.com/docker/go-plugins-helpers/blob/master/network/api.go
|
||||
|
||||
// CapabilitiesResponse returns whether or not this network is global or local
|
||||
type CapabilitiesResponse struct {
|
||||
Scope string
|
||||
ConnectivityScope string
|
||||
}
|
||||
|
||||
func (p *Plugin) apiGetCapabilities(w http.ResponseWriter, r *http.Request) {
|
||||
util.JSONResponse(w, CapabilitiesResponse{
|
||||
Scope: "local",
|
||||
ConnectivityScope: "global",
|
||||
}, http.StatusOK)
|
||||
}
|
||||
|
||||
// IPAMData contains IPv4 or IPv6 addressing information
|
||||
type IPAMData struct {
|
||||
AddressSpace string
|
||||
Pool string
|
||||
Gateway string
|
||||
AuxAddresses map[string]interface{}
|
||||
}
|
||||
|
||||
// CreateNetworkRequest is sent by the daemon when a network needs to be created
|
||||
type CreateNetworkRequest struct {
|
||||
NetworkID string
|
||||
Options map[string]interface{}
|
||||
IPv4Data []*IPAMData
|
||||
IPv6Data []*IPAMData
|
||||
}
|
||||
|
||||
func (p *Plugin) apiCreateNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
var req CreateNetworkRequest
|
||||
if err := util.ParseJSONBody(&req, w, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := p.CreateNetwork(req); err != nil {
|
||||
util.JSONErrResponse(w, err, 0)
|
||||
return
|
||||
}
|
||||
|
||||
util.JSONResponse(w, struct{}{}, http.StatusOK)
|
||||
}
|
||||
|
||||
// DeleteNetworkRequest is sent by the daemon when a network needs to be removed
|
||||
type DeleteNetworkRequest struct {
|
||||
NetworkID string
|
||||
}
|
||||
|
||||
func (p *Plugin) apiDeleteNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
var req DeleteNetworkRequest
|
||||
if err := util.ParseJSONBody(&req, w, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := p.DeleteNetwork(req); err != nil {
|
||||
util.JSONErrResponse(w, err, 0)
|
||||
return
|
||||
}
|
||||
|
||||
util.JSONResponse(w, struct{}{}, http.StatusOK)
|
||||
}
|
||||
|
||||
// EndpointInterface contains endpoint interface information
|
||||
type EndpointInterface struct {
|
||||
Address string
|
||||
AddressIPv6 string
|
||||
MacAddress string
|
||||
}
|
||||
|
||||
// CreateEndpointRequest is sent by the daemon when an endpoint should be created
|
||||
type CreateEndpointRequest struct {
|
||||
NetworkID string
|
||||
EndpointID string
|
||||
Interface *EndpointInterface
|
||||
Options map[string]interface{}
|
||||
}
|
||||
|
||||
// CreateEndpointResponse is sent as a response to a CreateEndpointRequest
|
||||
type CreateEndpointResponse struct {
|
||||
Interface *EndpointInterface
|
||||
}
|
||||
|
||||
func (p *Plugin) apiCreateEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
var req CreateEndpointRequest
|
||||
if err := util.ParseJSONBody(&req, w, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := p.CreateEndpoint(r.Context(), req)
|
||||
if err != nil {
|
||||
util.JSONErrResponse(w, err, 0)
|
||||
return
|
||||
}
|
||||
|
||||
util.JSONResponse(w, res, http.StatusOK)
|
||||
}
|
||||
|
||||
// InfoRequest is sent by the daemon when querying endpoint information
|
||||
type InfoRequest struct {
|
||||
NetworkID string
|
||||
EndpointID string
|
||||
}
|
||||
|
||||
// InfoResponse is endpoint information sent in response to an InfoRequest
|
||||
type InfoResponse struct {
|
||||
Value map[string]string
|
||||
}
|
||||
|
||||
func (p *Plugin) apiEndpointOperInfo(w http.ResponseWriter, r *http.Request) {
|
||||
var req InfoRequest
|
||||
if err := util.ParseJSONBody(&req, w, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := p.EndpointOperInfo(r.Context(), req)
|
||||
if err != nil {
|
||||
util.JSONErrResponse(w, err, 0)
|
||||
return
|
||||
}
|
||||
|
||||
util.JSONResponse(w, res, http.StatusOK)
|
||||
}
|
||||
|
||||
// DeleteEndpointRequest is sent by the daemon when an endpoint needs to be removed
|
||||
type DeleteEndpointRequest struct {
|
||||
NetworkID string
|
||||
EndpointID string
|
||||
}
|
||||
|
||||
func (p *Plugin) apiDeleteEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
var req DeleteEndpointRequest
|
||||
if err := util.ParseJSONBody(&req, w, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := p.DeleteEndpoint(req); err != nil {
|
||||
util.JSONErrResponse(w, err, 0)
|
||||
return
|
||||
}
|
||||
|
||||
util.JSONResponse(w, struct{}{}, http.StatusOK)
|
||||
}
|
||||
|
||||
// JoinRequest is sent by the Daemon when an endpoint needs be joined to a network
|
||||
type JoinRequest struct {
|
||||
NetworkID string
|
||||
EndpointID string
|
||||
SandboxKey string
|
||||
Options map[string]interface{}
|
||||
}
|
||||
|
||||
// InterfaceName consists of the name of the interface in the global netns and
|
||||
// the desired prefix to be appended to the interface inside the container netns
|
||||
type InterfaceName struct {
|
||||
SrcName string
|
||||
DstPrefix string
|
||||
}
|
||||
|
||||
// StaticRoute contains static route information
|
||||
type StaticRoute struct {
|
||||
Destination string
|
||||
RouteType int
|
||||
NextHop string
|
||||
}
|
||||
|
||||
// JoinResponse is sent in response to a JoinRequest
|
||||
type JoinResponse struct {
|
||||
InterfaceName InterfaceName
|
||||
Gateway string
|
||||
GatewayIPv6 string
|
||||
StaticRoutes []*StaticRoute
|
||||
DisableGatewayService bool
|
||||
}
|
||||
|
||||
func (p *Plugin) apiJoin(w http.ResponseWriter, r *http.Request) {
|
||||
var req JoinRequest
|
||||
if err := util.ParseJSONBody(&req, w, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := p.Join(r.Context(), req)
|
||||
if err != nil {
|
||||
util.JSONErrResponse(w, err, 0)
|
||||
return
|
||||
}
|
||||
|
||||
util.JSONResponse(w, res, http.StatusOK)
|
||||
}
|
||||
|
||||
// LeaveRequest is sent by the daemon when a endpoint is leaving a network
|
||||
type LeaveRequest struct {
|
||||
NetworkID string
|
||||
EndpointID string
|
||||
}
|
||||
|
||||
func (p *Plugin) apiLeave(w http.ResponseWriter, r *http.Request) {
|
||||
var req LeaveRequest
|
||||
if err := util.ParseJSONBody(&req, w, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := p.Leave(r.Context(), req); err != nil {
|
||||
util.JSONErrResponse(w, err, 0)
|
||||
return
|
||||
}
|
||||
|
||||
util.JSONResponse(w, struct{}{}, http.StatusOK)
|
||||
}
|
491
pkg/plugin/network.go
Normal file
491
pkg/plugin/network.go
Normal file
@ -0,0 +1,491 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
dTypes "github.com/docker/docker/api/types"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/devplayer0/docker-net-dhcp/pkg/udhcpc"
|
||||
"github.com/devplayer0/docker-net-dhcp/pkg/util"
|
||||
)
|
||||
|
||||
// CLIOptionsKey is the key used in create network options by the CLI for custom options
|
||||
const CLIOptionsKey string = "com.docker.network.generic"
|
||||
|
||||
// Implementations of the endpoints described in
|
||||
// https://github.com/moby/libnetwork/blob/master/docs/remote.md
|
||||
|
||||
// CreateNetwork "creates" a new DHCP network (just checks if the provided bridge exists and the null IPAM driver is
|
||||
// used)
|
||||
func (p *Plugin) CreateNetwork(r CreateNetworkRequest) error {
|
||||
log.WithField("options", r.Options).Debug("CreateNetwork options")
|
||||
|
||||
opts, err := decodeOpts(r.Options[util.OptionsKeyGeneric])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode network options: %w", err)
|
||||
}
|
||||
|
||||
if opts.Bridge == "" {
|
||||
return util.ErrBridgeRequired
|
||||
}
|
||||
|
||||
for _, d := range r.IPv4Data {
|
||||
if d.AddressSpace != "null" || d.Pool != "0.0.0.0/0" {
|
||||
return util.ErrIPAM
|
||||
}
|
||||
}
|
||||
|
||||
links, err := netlink.LinkList()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve list of network interfaces: %w", err)
|
||||
}
|
||||
|
||||
nets, err := p.docker.NetworkList(context.Background(), dTypes.NetworkListOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve list of networks from Docker: %w", err)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, l := range links {
|
||||
attrs := l.Attrs()
|
||||
if l.Type() != "bridge" || attrs.Name != opts.Bridge {
|
||||
continue
|
||||
}
|
||||
|
||||
v4Addrs, err := netlink.AddrList(l, unix.AF_INET)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve IPv4 addresses for %v: %w", attrs.Name, err)
|
||||
}
|
||||
v6Addrs, err := netlink.AddrList(l, unix.AF_INET6)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve IPv6 addresses for %v: %w", attrs.Name, err)
|
||||
}
|
||||
addrs := append(v4Addrs, v6Addrs...)
|
||||
|
||||
// Make sure the addresses on this bridge aren't used by another network
|
||||
for _, n := range nets {
|
||||
for _, c := range n.IPAM.Config {
|
||||
_, cidr, err := net.ParseCIDR(c.Subnet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse subnet %v on Docker network %v: %w", c.Subnet, n.ID, err)
|
||||
}
|
||||
|
||||
for _, linkAddr := range addrs {
|
||||
if linkAddr.IPNet.Contains(cidr.IP) || cidr.Contains(linkAddr.IP) {
|
||||
return util.ErrBridgeUsed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if !found {
|
||||
return util.ErrBridgeNotFound
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"network": r.NetworkID,
|
||||
"bridge": opts.Bridge,
|
||||
"ipv6": opts.IPv6,
|
||||
}).Info("Network created")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteNetwork "deletes" a DHCP network (does nothing, the bridge is managed by the user)
|
||||
func (p *Plugin) DeleteNetwork(r DeleteNetworkRequest) error {
|
||||
log.WithField("network", r.NetworkID).Info("Network deleted")
|
||||
return nil
|
||||
}
|
||||
|
||||
func vethPairNames(id string) (string, string) {
|
||||
return "dh-" + id[:12], id[:12] + "-dh"
|
||||
}
|
||||
|
||||
func (p *Plugin) netOptions(ctx context.Context, id string) (DHCPNetworkOptions, error) {
|
||||
dummy := DHCPNetworkOptions{}
|
||||
|
||||
n, err := p.docker.NetworkInspect(ctx, id, dTypes.NetworkInspectOptions{})
|
||||
if err != nil {
|
||||
return dummy, fmt.Errorf("failed to get info from Docker: %w", err)
|
||||
}
|
||||
|
||||
opts, err := decodeOpts(n.Options)
|
||||
if err != nil {
|
||||
return dummy, fmt.Errorf("failed to parse options: %w", err)
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// CreateEndpoint creates a veth pair and uses udhcpc to acquire an initial IP address on the container end. Docker will
|
||||
// move the interface into the container's namespace and apply the address.
|
||||
func (p *Plugin) CreateEndpoint(ctx context.Context, r CreateEndpointRequest) (CreateEndpointResponse, error) {
|
||||
log.WithField("options", r.Options).Debug("CreateEndpoint options")
|
||||
res := CreateEndpointResponse{
|
||||
Interface: &EndpointInterface{},
|
||||
}
|
||||
|
||||
if r.Interface != nil && (r.Interface.Address != "" || r.Interface.AddressIPv6 != "") {
|
||||
// TODO: Should we allow static IP's somehow?
|
||||
return res, util.ErrIPAM
|
||||
}
|
||||
|
||||
opts, err := p.netOptions(ctx, r.NetworkID)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("failed to get network options: %w", err)
|
||||
}
|
||||
|
||||
bridge, err := netlink.LinkByName(opts.Bridge)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("failed to get bridge interface: %w", err)
|
||||
}
|
||||
|
||||
hostName, ctrName := vethPairNames(r.EndpointID)
|
||||
la := netlink.NewLinkAttrs()
|
||||
la.Name = hostName
|
||||
hostLink := &netlink.Veth{
|
||||
LinkAttrs: la,
|
||||
PeerName: ctrName,
|
||||
}
|
||||
if r.Interface.MacAddress != "" {
|
||||
addr, err := net.ParseMAC(r.Interface.MacAddress)
|
||||
if err != nil {
|
||||
return res, util.ErrMACAddress
|
||||
}
|
||||
|
||||
hostLink.PeerHardwareAddr = addr
|
||||
}
|
||||
|
||||
if err := netlink.LinkAdd(hostLink); err != nil {
|
||||
return res, fmt.Errorf("failed to create veth pair: %w", err)
|
||||
}
|
||||
if err := func() error {
|
||||
if err := netlink.LinkSetUp(hostLink); err != nil {
|
||||
return fmt.Errorf("failed to set host side link of veth pair up: %w", err)
|
||||
}
|
||||
|
||||
ctrLink, err := netlink.LinkByName(ctrName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find container side of veth pair: %w", err)
|
||||
}
|
||||
if err := netlink.LinkSetUp(ctrLink); err != nil {
|
||||
return fmt.Errorf("failed to set container side link of veth pair up: %w", err)
|
||||
}
|
||||
|
||||
// Only write back the MAC address if it wasn't provided to us by libnetwork
|
||||
if r.Interface.MacAddress == "" {
|
||||
// The kernel will often reset a randomly assigned MAC address after actions like LinkSetMaster. We prevent
|
||||
// this behaviour by setting it manually to the random value
|
||||
if err := netlink.LinkSetHardwareAddr(ctrLink, ctrLink.Attrs().HardwareAddr); err != nil {
|
||||
return fmt.Errorf("failed to set container side of veth pair's MAC address: %w", err)
|
||||
}
|
||||
|
||||
res.Interface.MacAddress = ctrLink.Attrs().HardwareAddr.String()
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetMaster(hostLink, bridge); err != nil {
|
||||
return fmt.Errorf("failed to attach host side link of veth peer to bridge: %w", err)
|
||||
}
|
||||
|
||||
timeout := defaultLeaseTimeout
|
||||
if opts.LeaseTimeout != 0 {
|
||||
timeout = opts.LeaseTimeout
|
||||
}
|
||||
initialIP := func(v6 bool) error {
|
||||
v6str := ""
|
||||
if v6 {
|
||||
v6str = "v6"
|
||||
}
|
||||
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
info, err := udhcpc.GetIP(timeoutCtx, ctrName, &udhcpc.DHCPClientOptions{V6: v6})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get initial IP%v address via DHCP%v: %w", v6str, v6str, err)
|
||||
}
|
||||
ip, err := netlink.ParseAddr(info.IP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse initial IP%v address: %w", v6str, err)
|
||||
}
|
||||
|
||||
hint := p.joinHints[r.EndpointID]
|
||||
if v6 {
|
||||
res.Interface.AddressIPv6 = info.IP
|
||||
hint.IPv6 = ip
|
||||
// No gateways in DHCPv6!
|
||||
} else {
|
||||
res.Interface.Address = info.IP
|
||||
hint.IPv4 = ip
|
||||
hint.Gateway = info.Gateway
|
||||
}
|
||||
p.joinHints[r.EndpointID] = hint
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := initialIP(false); err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.IPv6 {
|
||||
if err := initialIP(true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}(); err != nil {
|
||||
// Be sure to clean up the veth pair if any of this fails
|
||||
netlink.LinkDel(hostLink)
|
||||
return res, err
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"network": r.NetworkID[:12],
|
||||
"endpoint": r.EndpointID[:12],
|
||||
"mac_address": res.Interface.MacAddress,
|
||||
"ip": res.Interface.Address,
|
||||
"ipv6": res.Interface.AddressIPv6,
|
||||
"gateway": fmt.Sprintf("%#v", p.joinHints[r.EndpointID].Gateway),
|
||||
}).Info("Endpoint created")
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type operInfo struct {
|
||||
Bridge string `mapstructure:"bridge"`
|
||||
HostVEth string `mapstructure:"veth_host"`
|
||||
HostVEthMAC string `mapstructure:"veth_host_mac"`
|
||||
}
|
||||
|
||||
// EndpointOperInfo retrieves some info about an existing endpoint
|
||||
func (p *Plugin) EndpointOperInfo(ctx context.Context, r InfoRequest) (InfoResponse, error) {
|
||||
res := InfoResponse{}
|
||||
|
||||
opts, err := p.netOptions(ctx, r.NetworkID)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("failed to get network options: %w", err)
|
||||
}
|
||||
|
||||
hostName, _ := vethPairNames(r.EndpointID)
|
||||
hostLink, err := netlink.LinkByName(hostName)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("failed to find host side of veth pair: %w", err)
|
||||
}
|
||||
|
||||
info := operInfo{
|
||||
Bridge: opts.Bridge,
|
||||
HostVEth: hostName,
|
||||
HostVEthMAC: hostLink.Attrs().HardwareAddr.String(),
|
||||
}
|
||||
if err := mapstructure.Decode(info, &res.Value); err != nil {
|
||||
return res, fmt.Errorf("failed to encode OperInfo: %w", err)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// DeleteEndpoint deletes the veth pair
|
||||
func (p *Plugin) DeleteEndpoint(r DeleteEndpointRequest) error {
|
||||
hostName, _ := vethPairNames(r.EndpointID)
|
||||
link, err := netlink.LinkByName(hostName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup host veth interface %v: %w", hostName, err)
|
||||
}
|
||||
|
||||
if err := netlink.LinkDel(link); err != nil {
|
||||
return fmt.Errorf("failed to delete veth pair: %w", err)
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"network": r.NetworkID[:12],
|
||||
"endpoint": r.EndpointID[:12],
|
||||
}).Info("Endpoint deleted")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Plugin) addRoutes(v6 bool, bridge netlink.Link, r JoinRequest, hint joinHint, res *JoinResponse) error {
|
||||
family := unix.AF_INET
|
||||
if v6 {
|
||||
family = unix.AF_INET6
|
||||
}
|
||||
|
||||
routes, err := netlink.RouteListFiltered(family, &netlink.Route{
|
||||
LinkIndex: bridge.Attrs().Index,
|
||||
Type: unix.RTN_UNICAST,
|
||||
}, netlink.RT_FILTER_OIF|netlink.RT_FILTER_TYPE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list routes: %w", err)
|
||||
}
|
||||
|
||||
logFields := log.Fields{
|
||||
"network": r.NetworkID[:12],
|
||||
"endpoint": r.EndpointID[:12],
|
||||
"sandbox": r.SandboxKey,
|
||||
}
|
||||
for _, route := range routes {
|
||||
if route.Dst == nil {
|
||||
// Default route
|
||||
switch family {
|
||||
case unix.AF_INET:
|
||||
if res.Gateway == "" {
|
||||
res.Gateway = route.Gw.String()
|
||||
log.
|
||||
WithFields(logFields).
|
||||
WithField("gateway", res.Gateway).
|
||||
Info("[Join] Setting IPv4 gateway retrieved from bridge interface on host routing table")
|
||||
}
|
||||
case unix.AF_INET6:
|
||||
if res.GatewayIPv6 == "" {
|
||||
res.GatewayIPv6 = route.Gw.String()
|
||||
log.
|
||||
WithFields(logFields).
|
||||
WithField("gateway", res.GatewayIPv6).
|
||||
Info("[Join] Setting IPv6 gateway retrieved from bridge interface on host routing table")
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if route.Protocol == unix.RTPROT_KERNEL ||
|
||||
(family == unix.AF_INET && route.Dst.Contains(hint.IPv4.IP)) ||
|
||||
(family == unix.AF_INET6 && route.Dst.Contains(hint.IPv6.IP)) {
|
||||
// Make sure to leave out the default on-link route created automatically for the IP(s) acquired by DHCP
|
||||
continue
|
||||
}
|
||||
|
||||
staticRoute := &StaticRoute{
|
||||
Destination: route.Dst.String(),
|
||||
// Default to an on-link route
|
||||
RouteType: 1,
|
||||
}
|
||||
res.StaticRoutes = append(res.StaticRoutes, staticRoute)
|
||||
|
||||
if route.Gw != nil {
|
||||
staticRoute.RouteType = 0
|
||||
staticRoute.NextHop = route.Gw.String()
|
||||
|
||||
log.
|
||||
WithFields(logFields).
|
||||
WithField("route", staticRoute.Destination).
|
||||
WithField("gateway", staticRoute.NextHop).
|
||||
Info("[Join] Adding route (via gateway) retrieved from bridge interface on host routing table")
|
||||
} else {
|
||||
log.
|
||||
WithFields(logFields).
|
||||
WithField("route", staticRoute.Destination).
|
||||
Info("[Join] Adding on-link route retrieved from bridge interface on host routing table")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Join passes the veth name and route information (gateway from DHCP and existing routes on the host bridge) to Docker
|
||||
// and starts a persistent DHCP client to maintain the lease on the acquired IP
|
||||
func (p *Plugin) Join(ctx context.Context, r JoinRequest) (JoinResponse, error) {
|
||||
log.WithField("options", r.Options).Debug("Join options")
|
||||
res := JoinResponse{}
|
||||
|
||||
opts, err := p.netOptions(ctx, r.NetworkID)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("failed to get network options: %w", err)
|
||||
}
|
||||
|
||||
_, ctrName := vethPairNames(r.EndpointID)
|
||||
|
||||
res.InterfaceName = InterfaceName{
|
||||
SrcName: ctrName,
|
||||
DstPrefix: opts.Bridge,
|
||||
}
|
||||
|
||||
hint, ok := p.joinHints[r.EndpointID]
|
||||
if !ok {
|
||||
return res, util.ErrNoHint
|
||||
}
|
||||
delete(p.joinHints, r.EndpointID)
|
||||
|
||||
if hint.Gateway != "" {
|
||||
log.WithFields(log.Fields{
|
||||
"network": r.NetworkID[:12],
|
||||
"endpoint": r.EndpointID[:12],
|
||||
"sandbox": r.SandboxKey,
|
||||
"gateway": hint.Gateway,
|
||||
}).Info("[Join] Setting IPv4 gateway retrieved from initial DHCP in CreateEndpoint")
|
||||
res.Gateway = hint.Gateway
|
||||
}
|
||||
|
||||
bridge, err := netlink.LinkByName(opts.Bridge)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("failed to get bridge interface: %w", err)
|
||||
}
|
||||
|
||||
if err := p.addRoutes(false, bridge, r, hint, &res); err != nil {
|
||||
return res, err
|
||||
}
|
||||
if opts.IPv6 {
|
||||
if err := p.addRoutes(true, bridge, r, hint, &res); err != nil {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
// TODO: Make timeout configurable?
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
m := newDHCPManager(p.docker, r, opts)
|
||||
m.LastIP = hint.IPv4
|
||||
m.LastIPv6 = hint.IPv6
|
||||
|
||||
if err := m.Start(ctx); err != nil {
|
||||
log.WithError(err).WithFields(log.Fields{
|
||||
"network": r.NetworkID[:12],
|
||||
"endpoint": r.EndpointID[:12],
|
||||
"sandbox": r.SandboxKey,
|
||||
}).Error("Failed to start persistent DHCP client")
|
||||
return
|
||||
}
|
||||
|
||||
p.persistentDHCP[r.EndpointID] = m
|
||||
}()
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"network": r.NetworkID[:12],
|
||||
"endpoint": r.EndpointID[:12],
|
||||
"sandbox": r.SandboxKey,
|
||||
}).Info("Joined sandbox to endpoint")
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Leave stops the persistent DHCP client for an endpoint
|
||||
func (p *Plugin) Leave(ctx context.Context, r LeaveRequest) error {
|
||||
manager, ok := p.persistentDHCP[r.EndpointID]
|
||||
if !ok {
|
||||
return util.ErrNoSandbox
|
||||
}
|
||||
delete(p.persistentDHCP, r.EndpointID)
|
||||
|
||||
if err := manager.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"network": r.NetworkID[:12],
|
||||
"endpoint": r.EndpointID[:12],
|
||||
}).Info("Sandbox left endpoint")
|
||||
|
||||
return nil
|
||||
}
|
120
pkg/plugin/plugin.go
Normal file
120
pkg/plugin/plugin.go
Normal file
@ -0,0 +1,120 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
docker "github.com/docker/docker/client"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/devplayer0/docker-net-dhcp/pkg/util"
|
||||
)
|
||||
|
||||
// DriverName is the name of the Docker Network Driver
|
||||
const DriverName string = "net-dhcp"
|
||||
|
||||
const defaultLeaseTimeout = 10 * time.Second
|
||||
|
||||
// DHCPNetworkOptions contains options for the DHCP network driver
|
||||
type DHCPNetworkOptions struct {
|
||||
Bridge string
|
||||
IPv6 bool
|
||||
LeaseTimeout time.Duration `mapstructure:"lease_timeout"`
|
||||
}
|
||||
|
||||
func decodeOpts(input interface{}) (DHCPNetworkOptions, error) {
|
||||
var opts DHCPNetworkOptions
|
||||
optsDecoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Result: &opts,
|
||||
ErrorUnused: true,
|
||||
WeaklyTypedInput: true,
|
||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
),
|
||||
})
|
||||
if err != nil {
|
||||
return opts, fmt.Errorf("failed to create options decoder: %w", err)
|
||||
}
|
||||
|
||||
if err := optsDecoder.Decode(input); err != nil {
|
||||
return opts, err
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
type joinHint struct {
|
||||
IPv4 *netlink.Addr
|
||||
IPv6 *netlink.Addr
|
||||
Gateway string
|
||||
}
|
||||
|
||||
// Plugin is the DHCP network plugin
|
||||
type Plugin struct {
|
||||
docker *docker.Client
|
||||
server http.Server
|
||||
|
||||
joinHints map[string]joinHint
|
||||
persistentDHCP map[string]*dhcpManager
|
||||
}
|
||||
|
||||
// NewPlugin creates a new Plugin
|
||||
func NewPlugin() (*Plugin, error) {
|
||||
client, err := docker.NewClient("unix:///run/docker.sock", "v1.13.1", nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create docker client: %w", err)
|
||||
}
|
||||
|
||||
p := Plugin{
|
||||
docker: client,
|
||||
|
||||
joinHints: make(map[string]joinHint),
|
||||
persistentDHCP: make(map[string]*dhcpManager),
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/NetworkDriver.GetCapabilities", p.apiGetCapabilities)
|
||||
|
||||
mux.HandleFunc("/NetworkDriver.CreateNetwork", p.apiCreateNetwork)
|
||||
mux.HandleFunc("/NetworkDriver.DeleteNetwork", p.apiDeleteNetwork)
|
||||
|
||||
mux.HandleFunc("/NetworkDriver.CreateEndpoint", p.apiCreateEndpoint)
|
||||
mux.HandleFunc("/NetworkDriver.EndpointOperInfo", p.apiEndpointOperInfo)
|
||||
mux.HandleFunc("/NetworkDriver.DeleteEndpoint", p.apiDeleteEndpoint)
|
||||
|
||||
mux.HandleFunc("/NetworkDriver.Join", p.apiJoin)
|
||||
mux.HandleFunc("/NetworkDriver.Leave", p.apiLeave)
|
||||
|
||||
p.server = http.Server{
|
||||
Handler: handlers.CustomLoggingHandler(nil, mux, util.WriteAccessLog),
|
||||
}
|
||||
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// Listen starts the plugin server
|
||||
func (p *Plugin) Listen(bindSock string) error {
|
||||
l, err := net.Listen("unix", bindSock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.server.Serve(l)
|
||||
}
|
||||
|
||||
// Close stops the plugin server
|
||||
func (p *Plugin) Close() error {
|
||||
if err := p.docker.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close docker client: %w", err)
|
||||
}
|
||||
|
||||
if err := p.server.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close http server: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
223
pkg/udhcpc/client.go
Normal file
223
pkg/udhcpc/client.go
Normal file
@ -0,0 +1,223 @@
|
||||
package udhcpc
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netns"
|
||||
|
||||
"github.com/devplayer0/docker-net-dhcp/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultHandler = "/usr/lib/net-dhcp/udhcpc-handler"
|
||||
VendorID = "docker-net-dhcp"
|
||||
)
|
||||
|
||||
type DHCPClientOptions struct {
|
||||
Hostname string
|
||||
V6 bool
|
||||
Once bool
|
||||
Namespace string
|
||||
|
||||
HandlerScript string
|
||||
}
|
||||
|
||||
// DHCPClient represents a udhcpc(6) client
|
||||
type DHCPClient struct {
|
||||
Opts *DHCPClientOptions
|
||||
|
||||
cmd *exec.Cmd
|
||||
eventPipe io.ReadCloser
|
||||
}
|
||||
|
||||
// NewDHCPClient creates a new udhcpc(6) client
|
||||
func NewDHCPClient(iface string, opts *DHCPClientOptions) (*DHCPClient, error) {
|
||||
if opts.HandlerScript == "" {
|
||||
opts.HandlerScript = DefaultHandler
|
||||
}
|
||||
|
||||
path := "udhcpc"
|
||||
if opts.V6 {
|
||||
path = "udhcpc6"
|
||||
}
|
||||
c := &DHCPClient{
|
||||
Opts: opts,
|
||||
// Foreground, set interface and handler "script"
|
||||
cmd: exec.Command(path, "-f", "-i", iface, "-s", opts.HandlerScript),
|
||||
}
|
||||
|
||||
stderrPipe, err := c.cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set up udhcpc stderr pipe: %w", err)
|
||||
}
|
||||
// Pipe udhcpc stderr (logs) to logrus at debug level
|
||||
go io.Copy(log.StandardLogger().WriterLevel(log.DebugLevel), stderrPipe)
|
||||
|
||||
if c.eventPipe, err = c.cmd.StdoutPipe(); err != nil {
|
||||
return nil, fmt.Errorf("failed to set up udhcpc stdout pipe: %w", err)
|
||||
}
|
||||
|
||||
if opts.Once {
|
||||
// Exit after obtaining lease
|
||||
c.cmd.Args = append(c.cmd.Args, "-q")
|
||||
} else {
|
||||
// Release IP address on exit
|
||||
c.cmd.Args = append(c.cmd.Args, "-R")
|
||||
}
|
||||
|
||||
if opts.Hostname != "" {
|
||||
hostnameOpt := "hostname:" + opts.Hostname
|
||||
if opts.V6 {
|
||||
// TODO: We encode the fqdn for DHCPv6 because udhcpc6 seems to be broken
|
||||
var data bytes.Buffer
|
||||
|
||||
// flags: S bit set (see RFC4704)
|
||||
binary.Write(&data, binary.BigEndian, uint8(0b0001))
|
||||
binary.Write(&data, binary.BigEndian, uint8(len(opts.Hostname)))
|
||||
data.WriteString(opts.Hostname)
|
||||
|
||||
hostnameOpt = "0x27:" + hex.EncodeToString(data.Bytes())
|
||||
}
|
||||
|
||||
c.cmd.Args = append(c.cmd.Args, "-x", hostnameOpt)
|
||||
}
|
||||
|
||||
// Vendor ID string option is not available for udhcpc6
|
||||
if !opts.V6 {
|
||||
c.cmd.Args = append(c.cmd.Args, "-V", VendorID)
|
||||
}
|
||||
|
||||
log.WithField("cmd", c.cmd).Trace("new udhcpc client")
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Start starts udhcpc(6)
|
||||
func (c *DHCPClient) Start() (chan Event, error) {
|
||||
if c.Opts.Namespace != "" {
|
||||
// Lock the OS Thread so we don't accidentally switch namespaces
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
origNS, err := netns.Get()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open current network namespace: %w", err)
|
||||
}
|
||||
defer origNS.Close()
|
||||
|
||||
ns, err := netns.GetFromPath(c.Opts.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open network namespace `%v`: %w", c.Opts.Namespace, err)
|
||||
}
|
||||
defer ns.Close()
|
||||
|
||||
if err := netns.Set(ns); err != nil {
|
||||
return nil, fmt.Errorf("failed to enter network namespace: %w", err)
|
||||
}
|
||||
|
||||
// Make sure we go back to the old namespace when we return
|
||||
defer netns.Set(origNS)
|
||||
}
|
||||
|
||||
if err := c.cmd.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events := make(chan Event)
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(c.eventPipe)
|
||||
for scanner.Scan() {
|
||||
log.WithField("line", string(scanner.Bytes())).Trace("udhcpc handler line")
|
||||
|
||||
// Each line is a JSON-encoded event
|
||||
var event Event
|
||||
if err := json.Unmarshal(scanner.Bytes(), &event); err != nil {
|
||||
log.WithError(err).Warn("Failed to decode udhcpc event")
|
||||
continue
|
||||
}
|
||||
|
||||
events <- event
|
||||
}
|
||||
}()
|
||||
|
||||
return events, nil
|
||||
}
|
||||
|
||||
// Finish sends SIGTERM to udhcpc(6) and waits for it to exit. SIGTERM will not
|
||||
// be sent if `Opts.Once` is set.
|
||||
func (c *DHCPClient) Finish(ctx context.Context) error {
|
||||
// If only running to get an IP once, udhcpc will terminate on its own
|
||||
if !c.Opts.Once {
|
||||
if err := c.cmd.Process.Signal(syscall.SIGTERM); err != nil {
|
||||
return fmt.Errorf("failed to send SIGTERM to udhcpc: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
errChan := make(chan error)
|
||||
go func() {
|
||||
errChan <- c.cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
c.cmd.Process.Kill()
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// GetIP is a convenience function that runs udhcpc(6) once and returns the IP
|
||||
// info obtained.
|
||||
func GetIP(ctx context.Context, iface string, opts *DHCPClientOptions) (Info, error) {
|
||||
dummy := Info{}
|
||||
|
||||
opts.Once = true
|
||||
client, err := NewDHCPClient(iface, opts)
|
||||
if err != nil {
|
||||
return dummy, fmt.Errorf("failed to create DHCP client: %w", err)
|
||||
}
|
||||
|
||||
events, err := client.Start()
|
||||
if err != nil {
|
||||
return dummy, fmt.Errorf("failed to start DHCP client: %w", err)
|
||||
}
|
||||
|
||||
var info *Info
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-events:
|
||||
switch event.Type {
|
||||
case "bound", "renew":
|
||||
info = &event.Data
|
||||
}
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer close(done)
|
||||
|
||||
if err := client.Finish(ctx); err != nil {
|
||||
return dummy, err
|
||||
}
|
||||
|
||||
if info == nil {
|
||||
return dummy, util.ErrNoLease
|
||||
}
|
||||
|
||||
return *info, nil
|
||||
}
|
12
pkg/udhcpc/handler.go
Normal file
12
pkg/udhcpc/handler.go
Normal file
@ -0,0 +1,12 @@
|
||||
package udhcpc
|
||||
|
||||
type Info struct {
|
||||
IP string
|
||||
Gateway string
|
||||
Domain string
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Type string
|
||||
Data Info
|
||||
}
|
5
pkg/util/docker.go
Normal file
5
pkg/util/docker.go
Normal file
@ -0,0 +1,5 @@
|
||||
package util
|
||||
|
||||
const (
|
||||
OptionsKeyGeneric = "com.docker.network.generic"
|
||||
)
|
39
pkg/util/errors.go
Normal file
39
pkg/util/errors.go
Normal file
@ -0,0 +1,39 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrIPAM indicates an unsupported IPAM driver was used
|
||||
ErrIPAM = errors.New("only the null IPAM driver is supported")
|
||||
// ErrBridgeRequired indicates a network bridge was not provided for network creation
|
||||
ErrBridgeRequired = errors.New("bridge required")
|
||||
// ErrBridgeNotFound indicates that a bridge could not be found
|
||||
ErrBridgeNotFound = errors.New("bridge not found")
|
||||
// ErrBridgeUsed indicates that a bridge is already in use
|
||||
ErrBridgeUsed = errors.New("bridge already in use by Docker")
|
||||
// ErrMACAddress indicates an invalid MAC address
|
||||
ErrMACAddress = errors.New("invalid MAC address")
|
||||
// ErrNoLease indicates a DHCP lease was not obtained from udhcpc
|
||||
ErrNoLease = errors.New("udhcpc did not output a lease")
|
||||
// ErrNoHint indicates missing state from the CreateEndpoint stage in Join
|
||||
ErrNoHint = errors.New("missing CreateEndpoint hints")
|
||||
// ErrNotVEth indicates a host link was unexpectedly not a veth interface
|
||||
ErrNotVEth = errors.New("host link is not a veth interface")
|
||||
// ErrNoContainer indicates a container was unexpectedly not found
|
||||
ErrNoContainer = errors.New("couldn't find container by endpoint on the network")
|
||||
// ErrNoSandbox indicates missing state from the Join stage
|
||||
ErrNoSandbox = errors.New("missing joined endpoint state")
|
||||
)
|
||||
|
||||
func ErrToStatus(err error) int {
|
||||
switch {
|
||||
case errors.Is(err, ErrIPAM), errors.Is(err, ErrBridgeRequired), errors.Is(err, ErrBridgeNotFound),
|
||||
errors.Is(err, ErrBridgeUsed), errors.Is(err, ErrMACAddress):
|
||||
return http.StatusBadRequest
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
}
|
33
pkg/util/general.go
Normal file
33
pkg/util/general.go
Normal file
@ -0,0 +1,33 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
func AwaitCondition(ctx context.Context, cond func() (bool, error), interval time.Duration) error {
|
||||
errChan := make(chan error)
|
||||
go func() {
|
||||
for {
|
||||
ok, err := cond()
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
if ok {
|
||||
errChan <- nil
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(interval)
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
15
pkg/util/http.go
Normal file
15
pkg/util/http.go
Normal file
@ -0,0 +1,15 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func WriteAccessLog(w io.Writer, params handlers.LogFormatterParams) {
|
||||
log.WithFields(log.Fields{
|
||||
"status": params.StatusCode,
|
||||
"resSize": params.Size,
|
||||
}).Tracef("%v %v", params.Request.Method, params.URL.RequestURI())
|
||||
}
|
61
pkg/util/json.go
Normal file
61
pkg/util/json.go
Normal file
@ -0,0 +1,61 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// JSONResponse Sends a JSON payload in response to a HTTP request
|
||||
func JSONResponse(w http.ResponseWriter, v interface{}, statusCode int) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
|
||||
if err := json.NewEncoder(w).Encode(v); err != nil {
|
||||
log.WithField("err", err).Error("Failed to serialize JSON payload")
|
||||
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprint(w, "Failed to serialize JSON payload")
|
||||
}
|
||||
}
|
||||
|
||||
type jsonError struct {
|
||||
Message string `json:"Err"`
|
||||
}
|
||||
|
||||
// JSONErrResponse Sends an `error` as a JSON object with a `message` property
|
||||
func JSONErrResponse(w http.ResponseWriter, err error, statusCode int) {
|
||||
log.WithError(err).Error("Error while processing request")
|
||||
|
||||
w.Header().Set("Content-Type", "application/problem+json")
|
||||
if statusCode == 0 {
|
||||
statusCode = ErrToStatus(err)
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
|
||||
enc := json.NewEncoder(w)
|
||||
enc.Encode(jsonError{err.Error()})
|
||||
}
|
||||
|
||||
// ParseJSONBody attempts to parse the request body as JSON
|
||||
func ParseJSONBody(v interface{}, w http.ResponseWriter, r *http.Request) error {
|
||||
//data, err := io.ReadAll(r.Body)
|
||||
//if err != nil {
|
||||
// JSONErrResponse(w, fmt.Errorf("failed to read request body: %w", err), 0)
|
||||
// return err
|
||||
//}
|
||||
|
||||
//log.WithField("body", string(data)).Debug("request body")
|
||||
|
||||
d := json.NewDecoder(r.Body)
|
||||
d.DisallowUnknownFields()
|
||||
if err := d.Decode(v); err != nil {
|
||||
//if err := json.Unmarshal(data, v); err != nil {
|
||||
JSONErrResponse(w, fmt.Errorf("failed to parse request body: %w", err), http.StatusBadRequest)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
66
pkg/util/netlink.go
Normal file
66
pkg/util/netlink.go
Normal file
@ -0,0 +1,66 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
func AwaitNetNS(ctx context.Context, path string, interval time.Duration) (netns.NsHandle, error) {
|
||||
var err error
|
||||
nsChan := make(chan netns.NsHandle)
|
||||
go func() {
|
||||
for {
|
||||
var ns netns.NsHandle
|
||||
ns, err = netns.GetFromPath(path)
|
||||
if err == nil {
|
||||
nsChan <- ns
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(interval)
|
||||
}
|
||||
}()
|
||||
|
||||
var dummy netns.NsHandle
|
||||
select {
|
||||
case ns := <-nsChan:
|
||||
return ns, nil
|
||||
case <-ctx.Done():
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("path", path).Error("Failed to await network namespace")
|
||||
}
|
||||
return dummy, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func AwaitLinkByIndex(ctx context.Context, handle *netlink.Handle, index int, interval time.Duration) (netlink.Link, error) {
|
||||
var err error
|
||||
linkChan := make(chan netlink.Link)
|
||||
go func() {
|
||||
for {
|
||||
var link netlink.Link
|
||||
link, err = handle.LinkByIndex(index)
|
||||
if err == nil {
|
||||
linkChan <- link
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(interval)
|
||||
}
|
||||
}()
|
||||
|
||||
var dummy netlink.Link
|
||||
select {
|
||||
case link := <-linkChan:
|
||||
return link, nil
|
||||
case <-ctx.Done():
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("index", index).Error("Failed to await link by index")
|
||||
}
|
||||
return dummy, ctx.Err()
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
flask==1.1.1
|
||||
pyroute2==0.5.6
|
||||
docker==4.0.2
|
||||
eventfd==0.2
|
||||
posix_ipc==1.0.4
|
204
scripts/push_multiarch_plugin.py
Executable file
204
scripts/push_multiarch_plugin.py
Executable file
@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import hashlib
|
||||
import argparse
|
||||
import os
|
||||
import tempfile
|
||||
import gzip
|
||||
import tarfile
|
||||
import concurrent.futures
|
||||
|
||||
import requests
|
||||
from docker_image import reference
|
||||
import dxf
|
||||
|
||||
MTYPE_PLUGIN_CONFIG = 'application/vnd.docker.plugin.v1+json'
|
||||
MTYPE_LAYER = 'application/vnd.docker.image.rootfs.diff.tar.gzip'
|
||||
MTYPE_MANIFEST = 'application/vnd.docker.distribution.manifest.v2+json'
|
||||
MTYPE_MANIFEST_LIST = 'application/vnd.docker.distribution.manifest.list.v2+json'
|
||||
|
||||
class Platform:
|
||||
def __init__(self, s):
|
||||
self.buildx = s
|
||||
|
||||
split = s.split('/')
|
||||
if len(split) < 2:
|
||||
raise Exception('Invalid platform format')
|
||||
|
||||
self.dirname = '_'.join(split)
|
||||
|
||||
self.os = split[0]
|
||||
self.architecture = split[1]
|
||||
|
||||
self.variant = None
|
||||
if len(split) > 3:
|
||||
raise Exception('Invalid platform format')
|
||||
elif len(split) == 3:
|
||||
self.variant = split[2]
|
||||
elif self.architecture == 'arm64':
|
||||
# Weird exception? (seen in alpine images)
|
||||
self.variant = 'v8'
|
||||
|
||||
@property
|
||||
def manifest(self):
|
||||
d = {
|
||||
'os': self.os,
|
||||
'architecture': self.architecture,
|
||||
}
|
||||
if self.variant is not None:
|
||||
d['variant'] = self.variant
|
||||
|
||||
return d
|
||||
@property
|
||||
def tag(self):
|
||||
if self.variant is not None:
|
||||
return f'{self.os}-{self.architecture}-{self.variant}'
|
||||
return f'{self.os}-{self.architecture}'
|
||||
|
||||
def __str__(self):
|
||||
return f'Platform(os={self.os}, architecture={self.architecture}, variant={self.variant})'
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def dxf_auth(reg: dxf.DXF, res):
|
||||
reg.authenticate(username=os.getenv('REGISTRY_USERNAME'), password=os.getenv('REGISTRY_PASSWORD'), response=res)
|
||||
|
||||
class DXF(dxf.DXF):
|
||||
def set_manifest(self, alias, manifest_json, mime=MTYPE_MANIFEST):
|
||||
"""
|
||||
Give a name (alias) to a manifest.
|
||||
|
||||
:param alias: Alias name
|
||||
:type alias: str
|
||||
|
||||
:param manifest_json: A V2 Schema 2 manifest JSON string
|
||||
:type digests: list
|
||||
"""
|
||||
self._request('put',
|
||||
'manifests/' + alias,
|
||||
data=manifest_json,
|
||||
headers={'Content-Type': mime})
|
||||
|
||||
def push_manifest(self, manifest_dict, ref=None, mime=MTYPE_MANIFEST):
|
||||
mf = json.dumps(manifest_dict, sort_keys=True).encode('utf-8')
|
||||
size = len(mf)
|
||||
digest = dxf.hash_bytes(mf)
|
||||
|
||||
if ref is None:
|
||||
ref = digest
|
||||
|
||||
self.set_manifest(ref, mf, mime=mime)
|
||||
return size, digest
|
||||
|
||||
def tar_filter(p: Platform):
|
||||
def f(info: tarfile.TarInfo):
|
||||
# Ensure there's no directory called '/'
|
||||
if info.name == '':
|
||||
info.name = '.'
|
||||
|
||||
# buildx messes up symlink targets...
|
||||
platform_prefix = f'/{p.dirname}'
|
||||
if info.issym() and info.linkname.startswith(platform_prefix):
|
||||
info.linkname = info.linkname[len(platform_prefix):]
|
||||
|
||||
return info
|
||||
return f
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Construct and push a multiarch Docker plugin')
|
||||
parser.add_argument('config', help='plugin config.json')
|
||||
parser.add_argument('rootfs', help='buildx rootfs parent directory')
|
||||
parser.add_argument('image', help='target image (registry/image:tag)')
|
||||
parser.add_argument('-p', '--platforms', default='linux/amd64', help='buildx platforms')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
platforms = [Platform(p) for p in args.platforms.split(',')]
|
||||
|
||||
ref = reference.Reference.parse(args.image)
|
||||
hostname, repo = ref.split_hostname()
|
||||
tag = ref['tag']
|
||||
|
||||
reg = DXF(hostname, repo, auth=dxf_auth)
|
||||
#print(reg.list_aliases())
|
||||
|
||||
print(f'Pushing config file `{args.config}`')
|
||||
config_size = os.path.getsize(args.config)
|
||||
config_digest = reg.push_blob(filename=args.config, check_exists=True)
|
||||
print(f'Pushed config as {config_digest}')
|
||||
|
||||
def push_platform(p):
|
||||
#with open(f'/tmp/{p.dirname}.tar.gz', mode='w+b') as f:
|
||||
with tempfile.TemporaryFile(mode='w+b', suffix='.tar.gz') as f:
|
||||
tar_name = f'{p.dirname}.tar'
|
||||
# Use gzip separately to force mtime=0 (deterministic gzipping!)
|
||||
with gzip.GzipFile(filename=tar_name, mode='w', fileobj=f, mtime=0) as gz:
|
||||
with tarfile.open(name=tar_name, mode='w', fileobj=gz) as tar:
|
||||
path = os.path.join(args.rootfs, p.dirname)
|
||||
print(f"tar'ing and gzip'ing {path}")
|
||||
tar.add(path, arcname='', filter=tar_filter(p))
|
||||
f.seek(0, os.SEEK_SET)
|
||||
|
||||
sha256 = hashlib.sha256()
|
||||
for chunk in iter(lambda: f.read(8192), b''):
|
||||
sha256.update(chunk)
|
||||
h = 'sha256:' + sha256.hexdigest()
|
||||
|
||||
layer_size = f.tell()
|
||||
f.seek(0, os.SEEK_SET)
|
||||
|
||||
print(f'Pushing {p.buildx} layer')
|
||||
layer_digest = reg.push_blob(data=f, digest=h, check_exists=True)
|
||||
print(f'Pushed {p.buildx} layer as {layer_digest}')
|
||||
|
||||
platform_tag = f'{tag}-{p.tag}'
|
||||
print(f'Pushing {p.buildx} manifest with tag {platform_tag}')
|
||||
size, digest = reg.push_manifest({
|
||||
'schemaVersion': 2,
|
||||
'mediaType': MTYPE_MANIFEST,
|
||||
'config': {
|
||||
'mediaType': MTYPE_PLUGIN_CONFIG,
|
||||
'size': config_size,
|
||||
'digest': config_digest,
|
||||
},
|
||||
'layers': [
|
||||
{
|
||||
'mediaType': MTYPE_LAYER,
|
||||
'size': layer_size,
|
||||
'digest': layer_digest,
|
||||
}
|
||||
],
|
||||
}, ref=platform_tag)
|
||||
print(f'Pushed {p.buildx} manifest with digest {digest}')
|
||||
|
||||
return size, digest
|
||||
|
||||
mf_list = {
|
||||
'schemaVersion': 2,
|
||||
'mediaType': MTYPE_MANIFEST_LIST,
|
||||
'manifests': [],
|
||||
}
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
fs = {executor.submit(push_platform, p): p for p in platforms}
|
||||
|
||||
for f in concurrent.futures.as_completed(fs):
|
||||
p = fs[f]
|
||||
try:
|
||||
size, digest = f.result()
|
||||
except Exception as ex:
|
||||
print(f'Exception pushing `{p.buildx}`: {ex}')
|
||||
continue
|
||||
|
||||
mf_list['manifests'].append({
|
||||
'mediaType': MTYPE_MANIFEST,
|
||||
'size': size,
|
||||
'digest': digest,
|
||||
'platform': p.manifest,
|
||||
})
|
||||
|
||||
print(f'Pushing {args.image} manifest list')
|
||||
reg.push_manifest(mf_list, ref=tag, mime=MTYPE_MANIFEST_LIST)
|
||||
print(f'Pushed {args.image}')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
2
scripts/requirements.txt
Normal file
2
scripts/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
python-dxf>7, <8
|
||||
docker-image-py>0.1,<0.2
|
38
test_env.sh
Executable file
38
test_env.sh
Executable file
@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
BRIDGE=net-dhcp
|
||||
BRIDGE_IP="10.123.0.1"
|
||||
DUMMY_IP="10.123.0.3"
|
||||
MASK="24"
|
||||
DHCP_RANGE="10.123.0.5,10.123.0.254,10s"
|
||||
BRIDGE_IP6="fd69::1"
|
||||
DUMMY_IP6="fd69::3"
|
||||
MASK6="64"
|
||||
DHCP6_RANGE="fd69::5,fd69::1000,64,10s"
|
||||
DOMAIN=cool-dhcp
|
||||
|
||||
quit() {
|
||||
ip link del "$BRIDGE"
|
||||
exit
|
||||
}
|
||||
|
||||
trap quit SIGINT SIGTERM
|
||||
|
||||
ip link add "$BRIDGE" type bridge
|
||||
ip link set up dev "$BRIDGE"
|
||||
ip addr add "$BRIDGE_IP/$MASK" dev "$BRIDGE"
|
||||
ip addr add "$BRIDGE_IP6/$MASK6" dev "$BRIDGE"
|
||||
|
||||
ip route add 10.223.0.0/24 dev "$BRIDGE"
|
||||
ip route add 10.224.0.0/24 via "$DUMMY_IP"
|
||||
ip route add fd42::0/64 dev "$BRIDGE"
|
||||
# TODO: This doesn't work right now because the route is added by Docker before
|
||||
# router advertisement stuff is done :/
|
||||
#ip route add fd43::0/64 via "$DUMMY_IP6"
|
||||
|
||||
dnsmasq --no-daemon --conf-file=/dev/null --dhcp-leasefile=/tmp/docker-net-dhcp.leases \
|
||||
--port=0 --interface="$BRIDGE" --bind-interfaces \
|
||||
--domain="$DOMAIN" \
|
||||
--dhcp-range="$DHCP_RANGE" \
|
||||
--dhcp-range="$DHCP6_RANGE" --enable-ra
|
||||
|
||||
quit
|
Loading…
Reference in New Issue
Block a user