Compare commits
92 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ad62f90e54 | ||
|
6de6b37406 | ||
|
7756b5ce04 | ||
|
19b0797ae2 | ||
|
8c7e58a231 | ||
|
6b5001bf0e | ||
|
e12b5029d7 | ||
|
86eea326db | ||
|
f251ddda98 | ||
|
f81d19e692 | ||
|
c728870b49 | ||
|
1e1f8819bf | ||
|
0733c83a0a | ||
|
c9e4786893 | ||
|
8c4cb7238c | ||
|
821180bdf1 | ||
|
d2fa4cc0b8 | ||
|
046e018c80 | ||
|
9f23e39fca | ||
|
088578b055 | ||
|
0d1d4fa052 | ||
|
ac0574a377 | ||
|
35ce0c5049 | ||
|
489f322514 | ||
|
f3eac80675 | ||
|
f21fd951ef | ||
|
99b3b40342 | ||
|
24fcef14ef | ||
|
f7d4658cf1 | ||
|
6ab338cf58 | ||
|
9a75468a32 | ||
|
6193210d85 | ||
|
941eabb605 | ||
|
311414e63a | ||
|
3ca08c4f12 | ||
|
ab19e7258f | ||
|
0255214d97 | ||
|
31ffaa0e71 | ||
|
61b52ce4ae | ||
|
d10b40acb0 | ||
|
6542c2ee94 | ||
|
9f088b87ee | ||
|
e513e6ca59 | ||
|
6261f507a3 | ||
|
0ab16e11b8 | ||
|
36643b77b4 | ||
|
e272d725a5 | ||
|
a8f4143f53 | ||
|
28d93fba90 | ||
|
1ab8523d8a | ||
|
5614d9158b | ||
|
305dc6ce91 | ||
|
cc7e94b07c | ||
|
259959c0a5 | ||
|
3422e8a40c | ||
|
d806fb9126 | ||
|
a25ab90e05 | ||
|
845df22a32 | ||
|
9f37a93859 | ||
|
3b898042cd | ||
|
01ce79c453 | ||
|
51f1ae94ef | ||
|
5d33c94d04 | ||
|
a3bf13711c | ||
|
f2c37b9de6 | ||
|
b59bc960f2 | ||
|
0263c985cf | ||
|
3f0404d9e3 | ||
|
58721e0c20 | ||
|
743fbb1da4 | ||
|
24dd4b54bf | ||
|
60f0ccd8a2 | ||
|
2ba1376400 | ||
|
e51a8c92cf | ||
|
6301503095 | ||
|
1d26921710 | ||
|
e232af1073 | ||
|
a504fe7195 | ||
|
4dc407f600 | ||
|
bd7c4e04d4 | ||
|
4528e0c374 | ||
|
d233d93cbf | ||
|
8fce69d373 | ||
|
e843262064 | ||
|
298a772d68 | ||
|
64ea86436f | ||
|
19abddf1fe | ||
|
166094b5ad | ||
|
2ac000c68a | ||
|
81f592de74 | ||
|
863628ffaa | ||
|
be1acb72ac |
36
.github/workflows/ci.yml
vendored
36
.github/workflows/ci.yml
vendored
@@ -3,6 +3,8 @@ name: CI
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
tags:
|
||||||
|
- "*"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
schedule:
|
schedule:
|
||||||
@@ -18,7 +20,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.7
|
go-version: 1.16.5
|
||||||
- name: Build
|
- name: Build
|
||||||
run: make
|
run: make
|
||||||
|
|
||||||
@@ -29,7 +31,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.7
|
go-version: 1.16.5
|
||||||
- name: Build kg and kgctl for all Linux Architectures
|
- name: Build kg and kgctl for all Linux Architectures
|
||||||
run: make all-build
|
run: make all-build
|
||||||
|
|
||||||
@@ -40,9 +42,11 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.7
|
go-version: 1.16.5
|
||||||
- name: Build kgctl for Darwin
|
- name: Build kgctl for Darwin amd64
|
||||||
run: make OS=darwin
|
run: make OS=darwin ARCH=amd64
|
||||||
|
- name: Build kgctl for Darwin arm64
|
||||||
|
run: make OS=darwin ARCH=arm64
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -51,7 +55,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.7
|
go-version: 1.16.5
|
||||||
- name: Build kgctl for Windows
|
- name: Build kgctl for Windows
|
||||||
run: make OS=windows
|
run: make OS=windows
|
||||||
|
|
||||||
@@ -62,10 +66,22 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.7
|
go-version: 1.16.5
|
||||||
- name: Run Unit Tests
|
- name: Run Unit Tests
|
||||||
run: make unit
|
run: make unit
|
||||||
|
|
||||||
|
e2e:
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.16.5
|
||||||
|
- name: Run e2e Tests
|
||||||
|
run: make e2e
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -73,7 +89,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.7
|
go-version: 1.16.5
|
||||||
- name: Lint Code
|
- name: Lint Code
|
||||||
run: make lint
|
run: make lint
|
||||||
|
|
||||||
@@ -84,7 +100,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.7
|
go-version: 1.16.5
|
||||||
- name: Enable Experimental Docker CLI
|
- name: Enable Experimental Docker CLI
|
||||||
run: |
|
run: |
|
||||||
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
|
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
|
||||||
@@ -113,7 +129,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.7
|
go-version: 1.16.5
|
||||||
- name: Enable Experimental Docker CLI
|
- name: Enable Experimental Docker CLI
|
||||||
run: |
|
run: |
|
||||||
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
|
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
|
||||||
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15.7
|
go-version: 1.16.5
|
||||||
- name: Make Directory with kgctl Binaries to Be Released
|
- name: Make Directory with kgctl Binaries to Be Released
|
||||||
run: make release
|
run: make release
|
||||||
- name: Publish Release
|
- name: Publish Release
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@
|
|||||||
.push*
|
.push*
|
||||||
bin/
|
bin/
|
||||||
tmp/
|
tmp/
|
||||||
|
e2e/kind.yaml*
|
||||||
|
10
Dockerfile
10
Dockerfile
@@ -1,14 +1,16 @@
|
|||||||
ARG FROM=alpine
|
ARG FROM=alpine
|
||||||
FROM alpine AS cni
|
FROM $FROM AS cni
|
||||||
ARG GOARCH
|
ARG GOARCH=amd64
|
||||||
|
ARG CNI_PLUGINS_VERSION=v0.9.1
|
||||||
RUN apk add --no-cache curl && \
|
RUN apk add --no-cache curl && \
|
||||||
curl -Lo cni.tar.gz https://github.com/containernetworking/plugins/releases/download/v0.7.5/cni-plugins-$GOARCH-v0.7.5.tgz && \
|
curl -Lo cni.tar.gz https://github.com/containernetworking/plugins/releases/download/$CNI_PLUGINS_VERSION/cni-plugins-linux-$GOARCH-$CNI_PLUGINS_VERSION.tgz && \
|
||||||
tar -xf cni.tar.gz
|
tar -xf cni.tar.gz
|
||||||
|
|
||||||
FROM $FROM
|
FROM $FROM
|
||||||
ARG GOARCH
|
ARG GOARCH
|
||||||
|
ARG ALPINE_VERSION=v3.12
|
||||||
LABEL maintainer="squat <lserven@gmail.com>"
|
LABEL maintainer="squat <lserven@gmail.com>"
|
||||||
RUN echo -e "https://alpine.global.ssl.fastly.net/alpine/v3.12/main\nhttps://alpine.global.ssl.fastly.net/alpine/v3.12/community" > /etc/apk/repositories && \
|
RUN echo -e "https://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/main\nhttps://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/community" > /etc/apk/repositories && \
|
||||||
apk add --no-cache ipset iptables ip6tables wireguard-tools
|
apk add --no-cache ipset iptables ip6tables wireguard-tools
|
||||||
COPY --from=cni bridge host-local loopback portmap /opt/cni/bin/
|
COPY --from=cni bridge host-local loopback portmap /opt/cni/bin/
|
||||||
COPY bin/linux/$GOARCH/kg /opt/bin/
|
COPY bin/linux/$GOARCH/kg /opt/bin/
|
||||||
|
89
Makefile
89
Makefile
@@ -1,9 +1,8 @@
|
|||||||
export GO111MODULE=on
|
export GO111MODULE=on
|
||||||
.PHONY: push container clean container-name container-latest push-latest fmt lint test unit vendor header generate client deepcopy informer lister openapi manifest manfest-latest manifest-annotate manifest manfest-latest manifest-annotate release
|
.PHONY: push container clean container-name container-latest push-latest fmt lint test unit vendor header generate crd client deepcopy informer lister manifest manfest-latest manifest-annotate manifest manfest-latest manifest-annotate release gen-docs e2e
|
||||||
|
|
||||||
OS ?= $(shell go env GOOS)
|
OS ?= $(shell go env GOOS)
|
||||||
ARCH ?= $(shell go env GOARCH)
|
ARCH ?= $(shell go env GOARCH)
|
||||||
ALL_OS := linux darwin windows
|
|
||||||
ALL_ARCH := amd64 arm arm64
|
ALL_ARCH := amd64 arm arm64
|
||||||
DOCKER_ARCH := "amd64" "arm v7" "arm64 v8"
|
DOCKER_ARCH := "amd64" "arm v7" "arm64 v8"
|
||||||
ifeq ($(OS),linux)
|
ifeq ($(OS),linux)
|
||||||
@@ -11,12 +10,12 @@ ifeq ($(OS),linux)
|
|||||||
else
|
else
|
||||||
BINS := bin/$(OS)/$(ARCH)/kgctl
|
BINS := bin/$(OS)/$(ARCH)/kgctl
|
||||||
endif
|
endif
|
||||||
RELEASE_BINS := $(addprefix bin/release/kgctl-, $(addprefix linux-, $(ALL_ARCH)) darwin-amd64 windows-amd64)
|
RELEASE_BINS := $(addprefix bin/release/kgctl-, $(addprefix linux-, $(ALL_ARCH)) darwin-amd64 darwin-arm64 windows-amd64)
|
||||||
CLIENT_BINS := $(addsuffix /kgctl, $(addprefix bin/, $(addprefix linux/, $(ALL_ARCH)) darwin/amd64 windows/amd64))
|
|
||||||
PROJECT := kilo
|
PROJECT := kilo
|
||||||
PKG := github.com/squat/$(PROJECT)
|
PKG := github.com/squat/$(PROJECT)
|
||||||
REGISTRY ?= index.docker.io
|
REGISTRY ?= index.docker.io
|
||||||
IMAGE ?= squat/$(PROJECT)
|
IMAGE ?= squat/$(PROJECT)
|
||||||
|
FULLY_QUALIFIED_IMAGE := $(REGISTRY)/$(IMAGE)
|
||||||
|
|
||||||
TAG := $(shell git describe --abbrev=0 --tags HEAD 2>/dev/null)
|
TAG := $(shell git describe --abbrev=0 --tags HEAD 2>/dev/null)
|
||||||
COMMIT := $(shell git rev-parse HEAD)
|
COMMIT := $(shell git rev-parse HEAD)
|
||||||
@@ -33,16 +32,21 @@ SRC := $(shell find . -type f -name '*.go' -not -path "./vendor/*")
|
|||||||
GO_FILES ?= $$(find . -name '*.go' -not -path './vendor/*')
|
GO_FILES ?= $$(find . -name '*.go' -not -path './vendor/*')
|
||||||
GO_PKGS ?= $$(go list ./... | grep -v "$(PKG)/vendor")
|
GO_PKGS ?= $$(go list ./... | grep -v "$(PKG)/vendor")
|
||||||
|
|
||||||
|
CONTROLLER_GEN_BINARY := bin/controller-gen
|
||||||
CLIENT_GEN_BINARY := bin/client-gen
|
CLIENT_GEN_BINARY := bin/client-gen
|
||||||
|
DOCS_GEN_BINARY := bin/docs-gen
|
||||||
DEEPCOPY_GEN_BINARY := bin/deepcopy-gen
|
DEEPCOPY_GEN_BINARY := bin/deepcopy-gen
|
||||||
INFORMER_GEN_BINARY := bin/informer-gen
|
INFORMER_GEN_BINARY := bin/informer-gen
|
||||||
LISTER_GEN_BINARY := bin/lister-gen
|
LISTER_GEN_BINARY := bin/lister-gen
|
||||||
OPENAPI_GEN_BINARY := bin/openapi-gen
|
|
||||||
GOLINT_BINARY := bin/golint
|
GOLINT_BINARY := bin/golint
|
||||||
EMBEDMD_BINARY := bin/embedmd
|
EMBEDMD_BINARY := bin/embedmd
|
||||||
|
KIND_BINARY := $(shell pwd)/bin/kind
|
||||||
|
KUBECTL_BINARY := $(shell pwd)/bin/kubectl
|
||||||
|
BASH_UNIT := $(shell pwd)/bin/bash_unit
|
||||||
|
BASH_UNIT_FLAGS :=
|
||||||
|
|
||||||
BUILD_IMAGE ?= golang:1.15.7-alpine
|
BUILD_IMAGE ?= golang:1.16.5-alpine
|
||||||
BASE_IMAGE ?= alpine:3.12
|
BASE_IMAGE ?= alpine:3.13
|
||||||
|
|
||||||
build: $(BINS)
|
build: $(BINS)
|
||||||
|
|
||||||
@@ -71,7 +75,13 @@ all-container-latest: $(addprefix container-latest-, $(ALL_ARCH))
|
|||||||
|
|
||||||
all-push-latest: $(addprefix push-latest-, $(ALL_ARCH))
|
all-push-latest: $(addprefix push-latest-, $(ALL_ARCH))
|
||||||
|
|
||||||
generate: client deepcopy informer lister openapi
|
generate: client deepcopy informer lister crd
|
||||||
|
|
||||||
|
crd: manifests/crds.yaml
|
||||||
|
manifests/crds.yaml: pkg/k8s/apis/kilo/v1alpha1/types.go $(CONTROLLER_GEN_BINARY)
|
||||||
|
$(CONTROLLER_GEN_BINARY) crd \
|
||||||
|
paths=./pkg/k8s/apis/kilo/... \
|
||||||
|
output:crd:stdout | tail -n +3 > $@
|
||||||
|
|
||||||
client: pkg/k8s/clientset/versioned/typed/kilo/v1alpha1/peer.go
|
client: pkg/k8s/clientset/versioned/typed/kilo/v1alpha1/peer.go
|
||||||
pkg/k8s/clientset/versioned/typed/kilo/v1alpha1/peer.go: .header pkg/k8s/apis/kilo/v1alpha1/types.go $(CLIENT_GEN_BINARY)
|
pkg/k8s/clientset/versioned/typed/kilo/v1alpha1/peer.go: .header pkg/k8s/apis/kilo/v1alpha1/types.go $(CLIENT_GEN_BINARY)
|
||||||
@@ -129,16 +139,9 @@ pkg/k8s/listers/kilo/v1alpha1/peer.go: .header pkg/k8s/apis/kilo/v1alpha1/types.
|
|||||||
rm -r github.com || true
|
rm -r github.com || true
|
||||||
go fmt ./pkg/k8s/listers/...
|
go fmt ./pkg/k8s/listers/...
|
||||||
|
|
||||||
openapi: pkg/k8s/apis/kilo/v1alpha1/openapi_generated.go
|
gen-docs: generate docs/api.md
|
||||||
pkg/k8s/apis/kilo/v1alpha1/openapi_generated.go: pkg/k8s/apis/kilo/v1alpha1/types.go $(OPENAPI_GEN_BINARY)
|
docs/api.md: pkg/k8s/apis/kilo/v1alpha1/types.go $(DOCS_GEN_BINARY)
|
||||||
$(OPENAPI_GEN_BINARY) \
|
$(DOCS_GEN_BINARY) $< > $@
|
||||||
--input-dirs $(PKG)/$(@D),k8s.io/apimachinery/pkg/apis/meta/v1,k8s.io/api/core/v1 \
|
|
||||||
--output-base $(CURDIR) \
|
|
||||||
--output-package ./$(@D) \
|
|
||||||
--logtostderr \
|
|
||||||
--report-filename /dev/null \
|
|
||||||
--go-header-file=.header
|
|
||||||
go fmt $@
|
|
||||||
|
|
||||||
$(BINS): $(SRC) go.mod
|
$(BINS): $(SRC) go.mod
|
||||||
@mkdir -p bin/$(word 2,$(subst /, ,$@))/$(word 3,$(subst /, ,$@))
|
@mkdir -p bin/$(word 2,$(subst /, ,$@))/$(word 3,$(subst /, ,$@))
|
||||||
@@ -191,7 +194,22 @@ lint: header $(GOLINT_BINARY)
|
|||||||
unit:
|
unit:
|
||||||
go test -mod=vendor --race ./...
|
go test -mod=vendor --race ./...
|
||||||
|
|
||||||
test: lint unit
|
test: lint unit e2e
|
||||||
|
|
||||||
|
$(KIND_BINARY):
|
||||||
|
curl -Lo $@ https://kind.sigs.k8s.io/dl/v0.11.1/kind-linux-$(ARCH)
|
||||||
|
chmod +x $@
|
||||||
|
|
||||||
|
$(KUBECTL_BINARY):
|
||||||
|
curl -Lo $@ https://dl.k8s.io/release/v1.21.0/bin/linux/$(ARCH)/kubectl
|
||||||
|
chmod +x $@
|
||||||
|
|
||||||
|
$(BASH_UNIT):
|
||||||
|
curl -Lo $@ https://raw.githubusercontent.com/pgrange/bash_unit/v1.7.2/bash_unit
|
||||||
|
chmod +x $@
|
||||||
|
|
||||||
|
e2e: container $(KIND_BINARY) $(KUBECTL_BINARY) $(BASH_UNIT) bin/$(OS)/$(ARCH)/kgctl
|
||||||
|
KILO_IMAGE=$(IMAGE):$(ARCH)-$(VERSION) KIND_BINARY=$(KIND_BINARY) KUBECTL_BINARY=$(KUBECTL_BINARY) KGCTL_BINARY=$(shell pwd)/bin/$(OS)/$(ARCH)/kgctl $(BASH_UNIT) $(BASH_UNIT_FLAGS) ./e2e/setup.sh ./e2e/full-mesh.sh ./e2e/location-mesh.sh ./e2e/multi-cluster.sh ./e2e/teardown.sh
|
||||||
|
|
||||||
header: .header
|
header: .header
|
||||||
@HEADER=$$(cat .header); \
|
@HEADER=$$(cat .header); \
|
||||||
@@ -226,8 +244,10 @@ website/docs/README.md: README.md
|
|||||||
sed -i 's/\.\/docs\///g' $@
|
sed -i 's/\.\/docs\///g' $@
|
||||||
find $(@D) -type f -name '*.md' | xargs -I{} sed -i 's/\.\/\(.\+\.svg\)/\/img\/\1/g' {}
|
find $(@D) -type f -name '*.md' | xargs -I{} sed -i 's/\.\/\(.\+\.svg\)/\/img\/\1/g' {}
|
||||||
sed -i 's/graphs\//\/img\/graphs\//g' $@
|
sed -i 's/graphs\//\/img\/graphs\//g' $@
|
||||||
|
# The next line is a workaround until mdx, docusaurus' markdown parser, can parse links with preceding brackets.
|
||||||
|
sed -i 's/\[\]\(\[.*\](.*)\)/\[\]\1/g' website/docs/api.md
|
||||||
|
|
||||||
website/build/index.html: website/docs/README.md
|
website/build/index.html: website/docs/README.md docs/api.md
|
||||||
yarn --cwd website install
|
yarn --cwd website install
|
||||||
yarn --cwd website build
|
yarn --cwd website build
|
||||||
|
|
||||||
@@ -243,7 +263,7 @@ container: .container-$(ARCH)-$(VERSION) container-name
|
|||||||
@docker images -q $(IMAGE):$(ARCH)-$(VERSION) > $@
|
@docker images -q $(IMAGE):$(ARCH)-$(VERSION) > $@
|
||||||
|
|
||||||
container-latest: .container-$(ARCH)-$(VERSION)
|
container-latest: .container-$(ARCH)-$(VERSION)
|
||||||
@docker tag $(IMAGE):$(ARCH)-$(VERSION) $(IMAGE):$(ARCH)-latest
|
@docker tag $(IMAGE):$(ARCH)-$(VERSION) $(FULLY_QUALIFIED_IMAGE):$(ARCH)-latest
|
||||||
@echo "container: $(IMAGE):$(ARCH)-latest"
|
@echo "container: $(IMAGE):$(ARCH)-latest"
|
||||||
|
|
||||||
container-name:
|
container-name:
|
||||||
@@ -251,14 +271,15 @@ container-name:
|
|||||||
|
|
||||||
manifest: .manifest-$(VERSION) manifest-name
|
manifest: .manifest-$(VERSION) manifest-name
|
||||||
.manifest-$(VERSION): Dockerfile $(addprefix push-, $(ALL_ARCH))
|
.manifest-$(VERSION): Dockerfile $(addprefix push-, $(ALL_ARCH))
|
||||||
@docker manifest create --amend $(IMAGE):$(VERSION) $(addsuffix -$(VERSION), $(addprefix squat/$(PROJECT):, $(ALL_ARCH)))
|
@docker manifest create --amend $(FULLY_QUALIFIED_IMAGE):$(VERSION) $(addsuffix -$(VERSION), $(addprefix $(FULLY_QUALIFIED_IMAGE):, $(ALL_ARCH)))
|
||||||
@$(MAKE) --no-print-directory manifest-annotate-$(VERSION)
|
@$(MAKE) --no-print-directory manifest-annotate-$(VERSION)
|
||||||
@docker manifest push $(IMAGE):$(VERSION) > $@
|
@docker manifest push $(FULLY_QUALIFIED_IMAGE):$(VERSION) > $@
|
||||||
|
|
||||||
manifest-latest: Dockerfile $(addprefix push-latest-, $(ALL_ARCH))
|
manifest-latest: Dockerfile $(addprefix push-latest-, $(ALL_ARCH))
|
||||||
@docker manifest create --amend $(IMAGE):latest $(addsuffix -latest, $(addprefix squat/$(PROJECT):, $(ALL_ARCH)))
|
@docker manifest rm $(FULLY_QUALIFIED_IMAGE):latest || echo no old manifest
|
||||||
|
@docker manifest create --amend $(FULLY_QUALIFIED_IMAGE):latest $(addsuffix -latest, $(addprefix $(FULLY_QUALIFIED_IMAGE):, $(ALL_ARCH)))
|
||||||
@$(MAKE) --no-print-directory manifest-annotate-latest
|
@$(MAKE) --no-print-directory manifest-annotate-latest
|
||||||
@docker manifest push $(IMAGE):latest
|
@docker manifest push $(FULLY_QUALIFIED_IMAGE):latest
|
||||||
@echo "manifest: $(IMAGE):latest"
|
@echo "manifest: $(IMAGE):latest"
|
||||||
|
|
||||||
manifest-annotate: manifest-annotate-$(VERSION)
|
manifest-annotate: manifest-annotate-$(VERSION)
|
||||||
@@ -269,7 +290,7 @@ manifest-annotate-%:
|
|||||||
annotate=; \
|
annotate=; \
|
||||||
j=0; for da in $(DOCKER_ARCH); do \
|
j=0; for da in $(DOCKER_ARCH); do \
|
||||||
if [ "$$j" -eq "$$i" ] && [ -n "$$da" ]; then \
|
if [ "$$j" -eq "$$i" ] && [ -n "$$da" ]; then \
|
||||||
annotate="docker manifest annotate $(IMAGE):$* $(IMAGE):$$a-$* --os linux --arch"; \
|
annotate="docker manifest annotate $(FULLY_QUALIFIED_IMAGE):$* $(FULLY_QUALIFIED_IMAGE):$$a-$* --os linux --arch"; \
|
||||||
k=0; for ea in $$da; do \
|
k=0; for ea in $$da; do \
|
||||||
[ "$$k" = 0 ] && annotate="$$annotate $$ea"; \
|
[ "$$k" = 0 ] && annotate="$$annotate $$ea"; \
|
||||||
[ "$$k" != 0 ] && annotate="$$annotate --variant $$ea"; \
|
[ "$$k" != 0 ] && annotate="$$annotate --variant $$ea"; \
|
||||||
@@ -283,15 +304,18 @@ manifest-annotate-%:
|
|||||||
done
|
done
|
||||||
|
|
||||||
manifest-name:
|
manifest-name:
|
||||||
@echo "manifest: $(IMAGE_ROOT):$(VERSION)"
|
@echo "manifest: $(IMAGE):$(VERSION)"
|
||||||
|
|
||||||
push: .push-$(ARCH)-$(VERSION) push-name
|
push: .push-$(ARCH)-$(VERSION) push-name
|
||||||
.push-$(ARCH)-$(VERSION): .container-$(ARCH)-$(VERSION)
|
.push-$(ARCH)-$(VERSION): .container-$(ARCH)-$(VERSION)
|
||||||
@docker push $(REGISTRY)/$(IMAGE):$(ARCH)-$(VERSION)
|
ifneq ($(REGISTRY),index.docker.io)
|
||||||
|
@docker tag $(IMAGE):$(ARCH)-$(VERSION) $(FULLY_QUALIFIED_IMAGE):$(ARCH)-$(VERSION)
|
||||||
|
endif
|
||||||
|
@docker push $(FULLY_QUALIFIED_IMAGE):$(ARCH)-$(VERSION)
|
||||||
@docker images -q $(IMAGE):$(ARCH)-$(VERSION) > $@
|
@docker images -q $(IMAGE):$(ARCH)-$(VERSION) > $@
|
||||||
|
|
||||||
push-latest: container-latest
|
push-latest: container-latest
|
||||||
@docker push $(REGISTRY)/$(IMAGE):$(ARCH)-latest
|
@docker push $(FULLY_QUALIFIED_IMAGE):$(ARCH)-latest
|
||||||
@echo "pushed: $(IMAGE):$(ARCH)-latest"
|
@echo "pushed: $(IMAGE):$(ARCH)-latest"
|
||||||
|
|
||||||
push-name:
|
push-name:
|
||||||
@@ -316,6 +340,9 @@ vendor:
|
|||||||
go mod tidy
|
go mod tidy
|
||||||
go mod vendor
|
go mod vendor
|
||||||
|
|
||||||
|
$(CONTROLLER_GEN_BINARY):
|
||||||
|
go build -mod=vendor -o $@ sigs.k8s.io/controller-tools/cmd/controller-gen
|
||||||
|
|
||||||
$(CLIENT_GEN_BINARY):
|
$(CLIENT_GEN_BINARY):
|
||||||
go build -mod=vendor -o $@ k8s.io/code-generator/cmd/client-gen
|
go build -mod=vendor -o $@ k8s.io/code-generator/cmd/client-gen
|
||||||
|
|
||||||
@@ -328,8 +355,8 @@ $(INFORMER_GEN_BINARY):
|
|||||||
$(LISTER_GEN_BINARY):
|
$(LISTER_GEN_BINARY):
|
||||||
go build -mod=vendor -o $@ k8s.io/code-generator/cmd/lister-gen
|
go build -mod=vendor -o $@ k8s.io/code-generator/cmd/lister-gen
|
||||||
|
|
||||||
$(OPENAPI_GEN_BINARY):
|
$(DOCS_GEN_BINARY): cmd/docs-gen/main.go
|
||||||
go build -mod=vendor -o $@ k8s.io/kube-openapi/cmd/openapi-gen
|
go build -mod=vendor -o $@ ./cmd/docs-gen
|
||||||
|
|
||||||
$(GOLINT_BINARY):
|
$(GOLINT_BINARY):
|
||||||
go build -mod=vendor -o $@ golang.org/x/lint/golint
|
go build -mod=vendor -o $@ golang.org/x/lint/golint
|
||||||
|
14
README.md
14
README.md
@@ -6,17 +6,20 @@ Kilo is a multi-cloud network overlay built on WireGuard and designed for Kubern
|
|||||||
|
|
||||||
[](https://github.com/squat/kilo/actions?query=workflow%3ACI)
|
[](https://github.com/squat/kilo/actions?query=workflow%3ACI)
|
||||||
[](https://goreportcard.com/report/github.com/squat/kilo)
|
[](https://goreportcard.com/report/github.com/squat/kilo)
|
||||||
|
[](https://hub.docker.com/r/squat/kilo)
|
||||||
|
[](https://slack.k8s.io/)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Kilo connects nodes in a cluster by providing an encrypted layer 3 network that can span across data centers and public clouds.
|
Kilo connects nodes in a cluster by providing an encrypted layer 3 network that can span across data centers and public clouds.
|
||||||
|
The Pod network created by Kilo is always fully connected, even when the nodes are in different networks or behind NAT.
|
||||||
By allowing pools of nodes in different locations to communicate securely, Kilo enables the operation of multi-cloud clusters.
|
By allowing pools of nodes in different locations to communicate securely, Kilo enables the operation of multi-cloud clusters.
|
||||||
Kilo's design allows clients to VPN to a cluster in order to securely access services running on the cluster.
|
Kilo's design allows clients to VPN to a cluster in order to securely access services running on the cluster.
|
||||||
In addition to creating multi-cloud clusters, Kilo enables the creation of multi-cluster services, i.e. services that span across different Kubernetes clusters.
|
In addition to creating multi-cloud clusters, Kilo enables the creation of multi-cluster services, i.e. services that span across different Kubernetes clusters.
|
||||||
|
|
||||||
An introductory video about Kilo from KubeCon EU 2019 can be found on [youtube](https://www.youtube.com/watch?v=iPz_DAOOCKA).
|
An introductory video about Kilo from KubeCon EU 2019 can be found on [youtube](https://www.youtube.com/watch?v=iPz_DAOOCKA).
|
||||||
|
|
||||||
## How it works
|
## How It Works
|
||||||
|
|
||||||
Kilo uses [WireGuard](https://www.wireguard.com/), a performant and secure VPN, to create a mesh between the different nodes in a cluster.
|
Kilo uses [WireGuard](https://www.wireguard.com/), a performant and secure VPN, to create a mesh between the different nodes in a cluster.
|
||||||
The Kilo agent, `kg`, runs on every node in the cluster, setting up the public and private keys for the VPN as well as the necessary rules to route packets between locations.
|
The Kilo agent, `kg`, runs on every node in the cluster, setting up the public and private keys for the VPN as well as the necessary rules to route packets between locations.
|
||||||
@@ -69,24 +72,28 @@ Kilo can be installed by deploying a DaemonSet to the cluster.
|
|||||||
To run Kilo on kubeadm:
|
To run Kilo on kubeadm:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/crds.yaml
|
||||||
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-kubeadm.yaml
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-kubeadm.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
To run Kilo on bootkube:
|
To run Kilo on bootkube:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/crds.yaml
|
||||||
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-bootkube.yaml
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-bootkube.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
To run Kilo on Typhoon:
|
To run Kilo on Typhoon:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/crds.yaml
|
||||||
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-typhoon.yaml
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-typhoon.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
To run Kilo on k3s:
|
To run Kilo on k3s:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/crds.yaml
|
||||||
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-k3s.yaml
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-k3s.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -99,6 +106,7 @@ Kilo currently supports running on top of Flannel.
|
|||||||
For example, to run Kilo on a Typhoon cluster running Flannel:
|
For example, to run Kilo on a Typhoon cluster running Flannel:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/crds.yaml
|
||||||
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-typhoon-flannel.yaml
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-typhoon-flannel.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -107,7 +115,7 @@ kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kil
|
|||||||
## VPN
|
## VPN
|
||||||
|
|
||||||
Kilo also enables peers outside of a Kubernetes cluster to connect to the VPN, allowing cluster applications to securely access external services and permitting developers and support to securely debug cluster resources.
|
Kilo also enables peers outside of a Kubernetes cluster to connect to the VPN, allowing cluster applications to securely access external services and permitting developers and support to securely debug cluster resources.
|
||||||
In order to declare a peer, start by defining a Kilo peer resource:
|
In order to declare a peer, start by defining a Kilo Peer resource:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cat <<'EOF' | kubectl apply -f -
|
cat <<'EOF' | kubectl apply -f -
|
||||||
@@ -148,7 +156,7 @@ for n in $(kubectl --kubeconfig $KUBECONFIG2 get no -o name | cut -d'/' -f2); do
|
|||||||
kgctl --kubeconfig $KUBECONFIG2 showconf node $n --as-peer -o yaml --allowed-ips $SERVICECIDR2 | kubectl --kubeconfig $KUBECONFIG1 apply -f -
|
kgctl --kubeconfig $KUBECONFIG2 showconf node $n --as-peer -o yaml --allowed-ips $SERVICECIDR2 | kubectl --kubeconfig $KUBECONFIG1 apply -f -
|
||||||
done
|
done
|
||||||
# Create a Service in cluster2 to mirror the Service in cluster1.
|
# Create a Service in cluster2 to mirror the Service in cluster1.
|
||||||
cat <<'EOF' | kubectl --kubeconfig $KUBECONFIG2 apply -f -
|
cat <<EOF | kubectl --kubeconfig $KUBECONFIG2 apply -f -
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
|
292
cmd/docs-gen/main.go
Normal file
292
cmd/docs-gen/main.go
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
// Copyright 2021 the Kilo authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// This file was adapted from https://github.com/prometheus-operator/prometheus-operator/blob/master/cmd/po-docgen/api.go.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/doc"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
firstParagraph = `# API
|
||||||
|
This document is a reference of the API types introduced by Kilo.
|
||||||
|
|
||||||
|
> **Note**: this document is generated from code comments. When contributing a change to this document, please do so by changing the code comments.`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
links = map[string]string{
|
||||||
|
"metav1.ObjectMeta": "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#objectmeta-v1-meta",
|
||||||
|
"metav1.ListMeta": "https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#listmeta-v1-meta",
|
||||||
|
}
|
||||||
|
|
||||||
|
selfLinks = map[string]string{}
|
||||||
|
typesDoc = map[string]KubeTypes{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func toSectionLink(name string) string {
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
name = strings.Replace(name, " ", "-", -1)
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTOC(types []KubeTypes) {
|
||||||
|
fmt.Printf("\n## Table of Contents\n")
|
||||||
|
for _, t := range types {
|
||||||
|
strukt := t[0]
|
||||||
|
if len(t) > 1 {
|
||||||
|
fmt.Printf("* [%s](#%s)\n", strukt.Name, toSectionLink(strukt.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printAPIDocs(paths []string) {
|
||||||
|
fmt.Println(firstParagraph)
|
||||||
|
|
||||||
|
types := parseDocumentationFrom(paths)
|
||||||
|
for _, t := range types {
|
||||||
|
strukt := t[0]
|
||||||
|
selfLinks[strukt.Name] = "#" + strings.ToLower(strukt.Name)
|
||||||
|
typesDoc[toLink(strukt.Name)] = t[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to parse once more to now add the self links and the inlined fields
|
||||||
|
types = parseDocumentationFrom(paths)
|
||||||
|
|
||||||
|
printTOC(types)
|
||||||
|
|
||||||
|
for _, t := range types {
|
||||||
|
strukt := t[0]
|
||||||
|
if len(t) > 1 {
|
||||||
|
fmt.Printf("\n## %s\n\n%s\n\n", strukt.Name, strukt.Doc)
|
||||||
|
|
||||||
|
fmt.Println("| Field | Description | Scheme | Required |")
|
||||||
|
fmt.Println("| ----- | ----------- | ------ | -------- |")
|
||||||
|
fields := t[1:]
|
||||||
|
for _, f := range fields {
|
||||||
|
fmt.Println("|", f.Name, "|", f.Doc, "|", f.Type, "|", f.Mandatory, "|")
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println("[Back to TOC](#table-of-contents)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pair of strings. We need the name of fields and the doc.
|
||||||
|
type Pair struct {
|
||||||
|
Name, Doc, Type string
|
||||||
|
Mandatory bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubeTypes is an array to represent all available types in a parsed file. [0] is for the type itself
|
||||||
|
type KubeTypes []Pair
|
||||||
|
|
||||||
|
// parseDocumentationFrom gets all types' documentation and returns them as an
|
||||||
|
// array. Each type is again represented as an array (we have to use arrays as we
|
||||||
|
// need to be sure of the order of the fields). This function returns fields and
|
||||||
|
// struct definitions that have no documentation as {name, ""}.
|
||||||
|
func parseDocumentationFrom(srcs []string) []KubeTypes {
|
||||||
|
var docForTypes []KubeTypes
|
||||||
|
|
||||||
|
for _, src := range srcs {
|
||||||
|
pkg := astFrom(src)
|
||||||
|
|
||||||
|
for _, kubType := range pkg.Types {
|
||||||
|
if structType, ok := kubType.Decl.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType); ok {
|
||||||
|
var ks KubeTypes
|
||||||
|
ks = append(ks, Pair{kubType.Name, fmtRawDoc(kubType.Doc), "", false})
|
||||||
|
|
||||||
|
for _, field := range structType.Fields.List {
|
||||||
|
// Skip fields that are not tagged.
|
||||||
|
if field.Tag == nil {
|
||||||
|
os.Stderr.WriteString(fmt.Sprintf("Tag is nil, skipping field: %v of type %v\n", field, field.Type))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Treat inlined fields separately as we don't want the original types to appear in the doc.
|
||||||
|
if isInlined(field) {
|
||||||
|
// Skip external types, as we don't want their content to be part of the API documentation.
|
||||||
|
if isInternalType(field.Type) {
|
||||||
|
ks = append(ks, typesDoc[fieldType(field.Type)]...)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
typeString := fieldType(field.Type)
|
||||||
|
fieldMandatory := fieldRequired(field)
|
||||||
|
if n := fieldName(field); n != "-" {
|
||||||
|
fieldDoc := fmtRawDoc(field.Doc.Text())
|
||||||
|
ks = append(ks, Pair{n, fieldDoc, typeString, fieldMandatory})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
docForTypes = append(docForTypes, ks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return docForTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
func astFrom(filePath string) *doc.Package {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
m := make(map[string]*ast.File)
|
||||||
|
|
||||||
|
f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m[filePath] = f
|
||||||
|
apkg, _ := ast.NewPackage(fset, m, nil, nil)
|
||||||
|
|
||||||
|
return doc.New(apkg, "", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fmtRawDoc(rawDoc string) string {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
delPrevChar := func() {
|
||||||
|
if buffer.Len() > 0 {
|
||||||
|
buffer.Truncate(buffer.Len() - 1) // Delete the last " " or "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore all lines after ---
|
||||||
|
rawDoc = strings.Split(rawDoc, "---")[0]
|
||||||
|
|
||||||
|
for _, line := range strings.Split(rawDoc, "\n") {
|
||||||
|
line = strings.TrimRight(line, " ")
|
||||||
|
leading := strings.TrimLeft(line, " ")
|
||||||
|
switch {
|
||||||
|
case len(line) == 0: // Keep paragraphs
|
||||||
|
delPrevChar()
|
||||||
|
buffer.WriteString("\n\n")
|
||||||
|
case strings.HasPrefix(leading, "TODO"): // Ignore one line TODOs
|
||||||
|
case strings.HasPrefix(leading, "+"): // Ignore instructions to go2idl
|
||||||
|
default:
|
||||||
|
if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") {
|
||||||
|
delPrevChar()
|
||||||
|
line = "\n" + line + "\n" // Replace it with newline. This is useful when we have a line with: "Example:\n\tJSON-someting..."
|
||||||
|
} else {
|
||||||
|
line += " "
|
||||||
|
}
|
||||||
|
buffer.WriteString(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
postDoc := strings.TrimRight(buffer.String(), "\n")
|
||||||
|
postDoc = strings.Replace(postDoc, "\\\"", "\"", -1) // replace user's \" to "
|
||||||
|
postDoc = strings.Replace(postDoc, "\"", "\\\"", -1) // Escape "
|
||||||
|
postDoc = strings.Replace(postDoc, "\n", "\\n", -1)
|
||||||
|
postDoc = strings.Replace(postDoc, "\t", "\\t", -1)
|
||||||
|
postDoc = strings.Replace(postDoc, "|", "\\|", -1)
|
||||||
|
|
||||||
|
return postDoc
|
||||||
|
}
|
||||||
|
|
||||||
|
func toLink(typeName string) string {
|
||||||
|
selfLink, hasSelfLink := selfLinks[typeName]
|
||||||
|
if hasSelfLink {
|
||||||
|
return wrapInLink(typeName, selfLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
link, hasLink := links[typeName]
|
||||||
|
if hasLink {
|
||||||
|
return wrapInLink(typeName, link)
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeName
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapInLink(text, link string) string {
|
||||||
|
return fmt.Sprintf("[%s](%s)", text, link)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isInlined(field *ast.Field) bool {
|
||||||
|
jsonTag := reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation
|
||||||
|
return strings.Contains(jsonTag, "inline")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isInternalType(typ ast.Expr) bool {
|
||||||
|
switch typ := typ.(type) {
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
pkg := typ.X.(*ast.Ident)
|
||||||
|
return strings.HasPrefix(pkg.Name, "monitoring")
|
||||||
|
case *ast.StarExpr:
|
||||||
|
return isInternalType(typ.X)
|
||||||
|
case *ast.ArrayType:
|
||||||
|
return isInternalType(typ.Elt)
|
||||||
|
case *ast.MapType:
|
||||||
|
return isInternalType(typ.Key) && isInternalType(typ.Value)
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldName returns the name of the field as it should appear in JSON format
|
||||||
|
// "-" indicates that this field is not part of the JSON representation
|
||||||
|
func fieldName(field *ast.Field) string {
|
||||||
|
jsonTag := reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation
|
||||||
|
jsonTag = strings.Split(jsonTag, ",")[0] // This can return "-"
|
||||||
|
if jsonTag == "" {
|
||||||
|
if field.Names != nil {
|
||||||
|
return field.Names[0].Name
|
||||||
|
}
|
||||||
|
return field.Type.(*ast.Ident).Name
|
||||||
|
}
|
||||||
|
return jsonTag
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldRequired returns whether a field is a required field.
|
||||||
|
func fieldRequired(field *ast.Field) bool {
|
||||||
|
jsonTag := ""
|
||||||
|
if field.Tag != nil {
|
||||||
|
jsonTag = reflect.StructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]).Get("json") // Delete first and last quotation
|
||||||
|
return !strings.Contains(jsonTag, "omitempty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldType(typ ast.Expr) string {
|
||||||
|
switch typ := typ.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
return toLink(typ.Name)
|
||||||
|
case *ast.StarExpr:
|
||||||
|
return "*" + toLink(fieldType(typ.X))
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
pkg := typ.X.(*ast.Ident)
|
||||||
|
t := typ.Sel
|
||||||
|
return toLink(pkg.Name + "." + t.Name)
|
||||||
|
case *ast.ArrayType:
|
||||||
|
return "[]" + toLink(fieldType(typ.Elt))
|
||||||
|
case *ast.MapType:
|
||||||
|
return "map[" + toLink(fieldType(typ.Key)) + "]" + toLink(fieldType(typ.Value))
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
printAPIDocs(os.Args[1:])
|
||||||
|
}
|
@@ -38,6 +38,11 @@ func runGraph(_ *cobra.Command, _ []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list peers: %v", err)
|
return fmt.Errorf("failed to list peers: %v", err)
|
||||||
}
|
}
|
||||||
|
// Obtain the Granularity by looking at the annotation of the first node.
|
||||||
|
if opts.granularity, err = optainGranularity(opts.granularity, ns); err != nil {
|
||||||
|
return fmt.Errorf("failed to obtain granularity: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var hostname string
|
var hostname string
|
||||||
subnet := mesh.DefaultKiloSubnet
|
subnet := mesh.DefaultKiloSubnet
|
||||||
nodes := make(map[string]*mesh.Node)
|
nodes := make(map[string]*mesh.Node)
|
||||||
@@ -60,7 +65,7 @@ func runGraph(_ *cobra.Command, _ []string) error {
|
|||||||
peers[p.Name] = p
|
peers[p.Name] = p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, 0, []byte{}, subnet, nodes[hostname].PersistentKeepalive)
|
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, 0, []byte{}, subnet, nodes[hostname].PersistentKeepalive, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create topology: %v", err)
|
return fmt.Errorf("failed to create topology: %v", err)
|
||||||
}
|
}
|
||||||
|
@@ -15,8 +15,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -46,6 +48,7 @@ var (
|
|||||||
availableGranularities = strings.Join([]string{
|
availableGranularities = strings.Join([]string{
|
||||||
string(mesh.LogicalGranularity),
|
string(mesh.LogicalGranularity),
|
||||||
string(mesh.FullGranularity),
|
string(mesh.FullGranularity),
|
||||||
|
string(mesh.AutoGranularity),
|
||||||
}, ", ")
|
}, ", ")
|
||||||
availableLogLevels = strings.Join([]string{
|
availableLogLevels = strings.Join([]string{
|
||||||
logLevelAll,
|
logLevelAll,
|
||||||
@@ -71,6 +74,7 @@ func runRoot(_ *cobra.Command, _ []string) error {
|
|||||||
switch opts.granularity {
|
switch opts.granularity {
|
||||||
case mesh.LogicalGranularity:
|
case mesh.LogicalGranularity:
|
||||||
case mesh.FullGranularity:
|
case mesh.FullGranularity:
|
||||||
|
case mesh.AutoGranularity:
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("mesh granularity %v unknown; posible values are: %s", granularity, availableGranularities)
|
return fmt.Errorf("mesh granularity %v unknown; posible values are: %s", granularity, availableGranularities)
|
||||||
}
|
}
|
||||||
@@ -108,8 +112,12 @@ func main() {
|
|||||||
Version: version.Version,
|
Version: version.Version,
|
||||||
}
|
}
|
||||||
cmd.PersistentFlags().StringVar(&backend, "backend", k8s.Backend, fmt.Sprintf("The backend for the mesh. Possible values: %s", availableBackends))
|
cmd.PersistentFlags().StringVar(&backend, "backend", k8s.Backend, fmt.Sprintf("The backend for the mesh. Possible values: %s", availableBackends))
|
||||||
cmd.PersistentFlags().StringVar(&granularity, "mesh-granularity", string(mesh.LogicalGranularity), fmt.Sprintf("The granularity of the network mesh to create. Possible values: %s", availableGranularities))
|
cmd.PersistentFlags().StringVar(&granularity, "mesh-granularity", string(mesh.AutoGranularity), fmt.Sprintf("The granularity of the network mesh to create. Possible values: %s", availableGranularities))
|
||||||
cmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", os.Getenv("KUBECONFIG"), "Path to kubeconfig.")
|
defaultKubeconfig := os.Getenv("KUBECONFIG")
|
||||||
|
if _, err := os.Stat(defaultKubeconfig); os.IsNotExist(err) {
|
||||||
|
defaultKubeconfig = filepath.Join(os.Getenv("HOME"), ".kube/config")
|
||||||
|
}
|
||||||
|
cmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", defaultKubeconfig, "Path to kubeconfig.")
|
||||||
cmd.PersistentFlags().Uint32Var(&opts.port, "port", mesh.DefaultKiloPort, "The WireGuard port over which the nodes communicate.")
|
cmd.PersistentFlags().Uint32Var(&opts.port, "port", mesh.DefaultKiloPort, "The WireGuard port over which the nodes communicate.")
|
||||||
cmd.PersistentFlags().StringVar(&topologyLabel, "topology-label", k8s.RegionLabelKey, "Kubernetes node label used to group nodes into logical locations.")
|
cmd.PersistentFlags().StringVar(&topologyLabel, "topology-label", k8s.RegionLabelKey, "Kubernetes node label used to group nodes into logical locations.")
|
||||||
|
|
||||||
@@ -125,3 +133,20 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func optainGranularity(gr mesh.Granularity, ns []*mesh.Node) (mesh.Granularity, error) {
|
||||||
|
if gr == mesh.AutoGranularity {
|
||||||
|
if len(ns) == 0 {
|
||||||
|
return gr, errors.New("could not get any nodes")
|
||||||
|
}
|
||||||
|
ret := mesh.Granularity(ns[0].Granularity)
|
||||||
|
switch ret {
|
||||||
|
case mesh.LogicalGranularity:
|
||||||
|
case mesh.FullGranularity:
|
||||||
|
default:
|
||||||
|
return ret, fmt.Errorf("mesh granularity %v is not supported", opts.granularity)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
return gr, nil
|
||||||
|
}
|
||||||
|
@@ -121,6 +121,10 @@ func runShowConfNode(_ *cobra.Command, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list peers: %v", err)
|
return fmt.Errorf("failed to list peers: %v", err)
|
||||||
}
|
}
|
||||||
|
// Obtain the Granularity by looking at the annotation of the first node.
|
||||||
|
if opts.granularity, err = optainGranularity(opts.granularity, ns); err != nil {
|
||||||
|
return fmt.Errorf("failed to obtain granularity: %w", err)
|
||||||
|
}
|
||||||
hostname := args[0]
|
hostname := args[0]
|
||||||
subnet := mesh.DefaultKiloSubnet
|
subnet := mesh.DefaultKiloSubnet
|
||||||
nodes := make(map[string]*mesh.Node)
|
nodes := make(map[string]*mesh.Node)
|
||||||
@@ -147,7 +151,7 @@ func runShowConfNode(_ *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, opts.port, []byte{}, subnet, nodes[hostname].PersistentKeepalive)
|
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, opts.port, []byte{}, subnet, nodes[hostname].PersistentKeepalive, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create topology: %v", err)
|
return fmt.Errorf("failed to create topology: %v", err)
|
||||||
}
|
}
|
||||||
@@ -208,6 +212,10 @@ func runShowConfPeer(_ *cobra.Command, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list peers: %v", err)
|
return fmt.Errorf("failed to list peers: %v", err)
|
||||||
}
|
}
|
||||||
|
// Obtain the Granularity by looking at the annotation of the first node.
|
||||||
|
if opts.granularity, err = optainGranularity(opts.granularity, ns); err != nil {
|
||||||
|
return fmt.Errorf("failed to obtain granularity: %w", err)
|
||||||
|
}
|
||||||
var hostname string
|
var hostname string
|
||||||
subnet := mesh.DefaultKiloSubnet
|
subnet := mesh.DefaultKiloSubnet
|
||||||
nodes := make(map[string]*mesh.Node)
|
nodes := make(map[string]*mesh.Node)
|
||||||
@@ -236,7 +244,7 @@ func runShowConfPeer(_ *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("did not find any peer named %q in the cluster", peer)
|
return fmt.Errorf("did not find any peer named %q in the cluster", peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, mesh.DefaultKiloPort, []byte{}, subnet, peers[peer].PersistentKeepalive)
|
t, err := mesh.NewTopology(nodes, peers, opts.granularity, hostname, mesh.DefaultKiloPort, []byte{}, subnet, peers[peer].PersistentKeepalive, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create topology: %v", err)
|
return fmt.Errorf("failed to create topology: %v", err)
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ The following annotations can be added to any Kubernetes Node object to configur
|
|||||||
|[kilo.squat.ai/leader](#leader)|string|`""`, `true`|
|
|[kilo.squat.ai/leader](#leader)|string|`""`, `true`|
|
||||||
|[kilo.squat.ai/location](#location)|string|`gcp-east`, `lab`|
|
|[kilo.squat.ai/location](#location)|string|`gcp-east`, `lab`|
|
||||||
|[kilo.squat.ai/persistent-keepalive](#persistent-keepalive)|uint|`10`|
|
|[kilo.squat.ai/persistent-keepalive](#persistent-keepalive)|uint|`10`|
|
||||||
|
|[kilo.squat.ai/allowed-location-ips](#allowed-location-ips)|CIDR|`66.66.66.66/32`|
|
||||||
|
|
||||||
### force-endpoint
|
### force-endpoint
|
||||||
In order to create links between locations, Kilo requires at least one node in each location to have an endpoint, ie a `host:port` combination, that is routable from the other locations.
|
In order to create links between locations, Kilo requires at least one node in each location to have an endpoint, ie a `host:port` combination, that is routable from the other locations.
|
||||||
@@ -35,7 +36,7 @@ In some situations it may be desirable to manually select the leader for a locat
|
|||||||
* _firewall_: Kilo requires an open UDP port, which defaults to 51820, to communicate between locations; if only one node is configured to have that port open, then that node should be given the leader annotation;
|
* _firewall_: Kilo requires an open UDP port, which defaults to 51820, to communicate between locations; if only one node is configured to have that port open, then that node should be given the leader annotation;
|
||||||
* _bandwidth_: if certain nodes in the cluster have a higher bandwidth or lower latency Internet connection, then those nodes should be given the leader annotation.
|
* _bandwidth_: if certain nodes in the cluster have a higher bandwidth or lower latency Internet connection, then those nodes should be given the leader annotation.
|
||||||
|
|
||||||
_Note_: multiple nodes within a single location can be given the leader annotation; in this case, Kilo will select one leader from the set of annotated nodes.
|
> **Note**: multiple nodes within a single location can be given the leader annotation; in this case, Kilo will select one leader from the set of annotated nodes.
|
||||||
|
|
||||||
### location
|
### location
|
||||||
Kilo allows nodes in different logical or physical locations to route packets to one-another.
|
Kilo allows nodes in different logical or physical locations to route packets to one-another.
|
||||||
@@ -43,7 +44,7 @@ In order to know what connections to create, Kilo needs to know which nodes are
|
|||||||
Kilo will try to infer each node's location from the [topology.kubernetes.io/region](https://kubernetes.io/docs/reference/kubernetes-api/labels-annotations-taints/#topologykubernetesioregion) node label.
|
Kilo will try to infer each node's location from the [topology.kubernetes.io/region](https://kubernetes.io/docs/reference/kubernetes-api/labels-annotations-taints/#topologykubernetesioregion) node label.
|
||||||
If the label is not present for a node, for example if running a bare-metal cluster or on an unsupported cloud provider, then the location annotation should be specified.
|
If the label is not present for a node, for example if running a bare-metal cluster or on an unsupported cloud provider, then the location annotation should be specified.
|
||||||
|
|
||||||
_Note_: all nodes without a defined location will be considered to be in the default location `""`.
|
> **Note**: all nodes without a defined location will be considered to be in the default location `""`.
|
||||||
|
|
||||||
### persistent-keepalive
|
### persistent-keepalive
|
||||||
In certain deployments, cluster nodes may be located behind NAT or a firewall, e.g. edge nodes located behind a commodity router.
|
In certain deployments, cluster nodes may be located behind NAT or a firewall, e.g. edge nodes located behind a commodity router.
|
||||||
@@ -52,3 +53,10 @@ In order for a node behind NAT to receive packets from nodes outside of the NATe
|
|||||||
The frequency of emission of these keepalive packets can be controlled by setting the persistent-keepalive annotation on the node behind NAT.
|
The frequency of emission of these keepalive packets can be controlled by setting the persistent-keepalive annotation on the node behind NAT.
|
||||||
The annotated node will use the specified value will as the persistent-keepalive interval for all of its peers.
|
The annotated node will use the specified value will as the persistent-keepalive interval for all of its peers.
|
||||||
For more background, [see the WireGuard documentation on NAT and firewall traversal](https://www.wireguard.com/quickstart/#nat-and-firewall-traversal-persistence).
|
For more background, [see the WireGuard documentation on NAT and firewall traversal](https://www.wireguard.com/quickstart/#nat-and-firewall-traversal-persistence).
|
||||||
|
|
||||||
|
### allowed-location-ips
|
||||||
|
It is possible to add allowed-location-ips to a location by annotating any node within that location.
|
||||||
|
Adding allowed-location-ips to a location makes these IPs routable from other locations as well.
|
||||||
|
|
||||||
|
In an example deployment of Kilo with two locations A and B, a printer in location A can be accessible from nodes and pods in location B.
|
||||||
|
Additionally, Kilo Peers can use the printer in location A.
|
||||||
|
69
docs/api.md
Normal file
69
docs/api.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# API
|
||||||
|
This document is a reference of the API types introduced by Kilo.
|
||||||
|
|
||||||
|
> **Note**: this document is generated from code comments. When contributing a change to this document, please do so by changing the code comments.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
* [DNSOrIP](#dnsorip)
|
||||||
|
* [Peer](#peer)
|
||||||
|
* [PeerEndpoint](#peerendpoint)
|
||||||
|
* [PeerList](#peerlist)
|
||||||
|
* [PeerSpec](#peerspec)
|
||||||
|
|
||||||
|
## DNSOrIP
|
||||||
|
|
||||||
|
DNSOrIP represents either a DNS name or an IP address. When both are given, the IP address, as it is more specific, override the DNS name.
|
||||||
|
|
||||||
|
| Field | Description | Scheme | Required |
|
||||||
|
| ----- | ----------- | ------ | -------- |
|
||||||
|
| dns | DNS must be a valid RFC 1123 subdomain. | string | false |
|
||||||
|
| ip | IP must be a valid IP address. | string | false |
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
## Peer
|
||||||
|
|
||||||
|
Peer is a WireGuard peer that should have access to the VPN.
|
||||||
|
|
||||||
|
| Field | Description | Scheme | Required |
|
||||||
|
| ----- | ----------- | ------ | -------- |
|
||||||
|
| metadata | Standard object’s metadata. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata | [metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#objectmeta-v1-meta) | false |
|
||||||
|
| spec | Specification of the desired behavior of the Kilo Peer. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status | [PeerSpec](#peerspec) | true |
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
## PeerEndpoint
|
||||||
|
|
||||||
|
PeerEndpoint represents a WireGuard endpoint, which is an IP:port tuple.
|
||||||
|
|
||||||
|
| Field | Description | Scheme | Required |
|
||||||
|
| ----- | ----------- | ------ | -------- |
|
||||||
|
| dnsOrIP | DNSOrIP is a DNS name or an IP address. | [DNSOrIP](#dnsorip) | true |
|
||||||
|
| port | Port must be a valid port number. | uint32 | true |
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
## PeerList
|
||||||
|
|
||||||
|
PeerList is a list of peers.
|
||||||
|
|
||||||
|
| Field | Description | Scheme | Required |
|
||||||
|
| ----- | ----------- | ------ | -------- |
|
||||||
|
| metadata | Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | [metav1.ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#listmeta-v1-meta) | false |
|
||||||
|
| items | List of peers. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md | [][Peer](#peer) | true |
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
||||||
|
|
||||||
|
## PeerSpec
|
||||||
|
|
||||||
|
PeerSpec is the description and configuration of a peer.
|
||||||
|
|
||||||
|
| Field | Description | Scheme | Required |
|
||||||
|
| ----- | ----------- | ------ | -------- |
|
||||||
|
| allowedIPs | AllowedIPs is the list of IP addresses that are allowed for the given peer's tunnel. | []string | true |
|
||||||
|
| endpoint | Endpoint is the initial endpoint for connections to the peer. | *[PeerEndpoint](#peerendpoint) | false |
|
||||||
|
| persistentKeepalive | PersistentKeepalive is the interval in seconds of the emission of keepalive packets by the peer. This defaults to 0, which disables the feature. | int | false |
|
||||||
|
| presharedKey | PresharedKey is the optional symmetric encryption key for the peer. | string | false |
|
||||||
|
| publicKey | PublicKey is the WireGuard public key for the peer. | string | true |
|
||||||
|
|
||||||
|
[Back to TOC](#table-of-contents)
|
100
docs/building_kilo.md
Normal file
100
docs/building_kilo.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# Build and Test Kilo
|
||||||
|
|
||||||
|
This document describes how you can build and test Kilo.
|
||||||
|
|
||||||
|
To follow along, you need to install the following utilities:
|
||||||
|
- `go` not for building but formatting the code and running unit tests
|
||||||
|
- `make`
|
||||||
|
- `jq`
|
||||||
|
- `git`
|
||||||
|
- `curl`
|
||||||
|
- `docker`
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Clone the Repository and `cd` into it.
|
||||||
|
```shell
|
||||||
|
git clone https://github.com/squat/kilo.git
|
||||||
|
cd kilo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
For consistency, the Kilo binaries are compiled in a Docker container, so make sure the `docker` package is installed and the daemon is running.
|
||||||
|
|
||||||
|
### Compile Binaries
|
||||||
|
|
||||||
|
To compile the `kg` and `kgctl` binaries run:
|
||||||
|
```shell
|
||||||
|
make
|
||||||
|
```
|
||||||
|
Binaries are always placed in a directory corresponding to the local system's OS and architecture following the pattern `bin/<os>/<architecture>/`, so on an AMD64 machine running Linux, the binaries will be stored in `bin/linux/amd64/`.
|
||||||
|
|
||||||
|
You can build the binaries for a different architecture by setting the `ARCH` environment variable before invoking `make`, e.g.:
|
||||||
|
```shell
|
||||||
|
ARCH=<arm|arm64|amd64> make
|
||||||
|
```
|
||||||
|
|
||||||
|
Likewise, to build `kg` for another OS, set the `OS` environment variable before invoking `make`:
|
||||||
|
```shell
|
||||||
|
OS=<windows|darwin|linux> make
|
||||||
|
```
|
||||||
|
## Test
|
||||||
|
|
||||||
|
To execute the unit tests, run:
|
||||||
|
```shell
|
||||||
|
make unit
|
||||||
|
```
|
||||||
|
|
||||||
|
To lint the code in the repository, run:
|
||||||
|
```shell
|
||||||
|
make lint
|
||||||
|
```
|
||||||
|
|
||||||
|
To execute basic end to end tests, run:
|
||||||
|
```shell
|
||||||
|
make e2e
|
||||||
|
```
|
||||||
|
> **Note**: The end to end tests are currently flaky, so try running them again if they fail.
|
||||||
|
|
||||||
|
To instead run all of the tests with a single command, run:
|
||||||
|
```shell
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build and Push the Container Images
|
||||||
|
|
||||||
|
If you want to build containers for a processor architecture that is different from your computer's, then you will first need to configure QEMU as the interpreter for binaries built for non-native architectures:
|
||||||
|
```shell
|
||||||
|
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
|
```
|
||||||
|
|
||||||
|
Set the `$IMAGE` environment variable to `<your Docker Hub user name>/kilo`.
|
||||||
|
This way the generated container images and manifests will be named accordingly.
|
||||||
|
By skipping this step, you will be able to tag images but will not be able to push the containers and manifests to your own Docker Hub.
|
||||||
|
```shell
|
||||||
|
export IMAGE=<docker hub user name>/kilo
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to use a different container registry, run:
|
||||||
|
```shell
|
||||||
|
export REGISTRY=<your registry without a trailing slash>
|
||||||
|
```
|
||||||
|
|
||||||
|
To build containers with the `kg` image for `arm`, `arm64` and `amd64`, run:
|
||||||
|
```shell
|
||||||
|
make all-container
|
||||||
|
```
|
||||||
|
|
||||||
|
Push the container images and build a manifest with:
|
||||||
|
```shell
|
||||||
|
make manifest
|
||||||
|
```
|
||||||
|
|
||||||
|
To tag and push the manifest with `latest`, run:
|
||||||
|
```shell
|
||||||
|
make manifest-latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can deploy the custom build of Kilo to your cluster.
|
||||||
|
If you are already running Kilo, change the image from `squat/kilo` to `[registry/]<username>/kilo[:sha]`.
|
44
docs/building_website.md
Normal file
44
docs/building_website.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Build and Test the Website
|
||||||
|
|
||||||
|
You may have noticed that the `markdown` files in the `/docs` directory are also displayed on [Kilo's website](https://kilo.squat.ai/).
|
||||||
|
If you want to add documentation to Kilo, you can start a local webserver to check out how the website would look like.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Install [yarn](https://yarnpkg.com/getting-started/install).
|
||||||
|
|
||||||
|
## Build and Run
|
||||||
|
|
||||||
|
The markdown files for the website are located in `/website/docs` and are generated from the like-named markdown files in the `/docs` directory and from the corresponding header files without the `.md` extension in the `/website/docs` directory.
|
||||||
|
To generate the markdown files in `/website/docs`, run:
|
||||||
|
```shell
|
||||||
|
make website/docs/README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, build the website itself by installing the `node_modules` and building the website's HTML from the generated markdown:
|
||||||
|
```shell
|
||||||
|
make website/build/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, start the website server with:
|
||||||
|
```shell
|
||||||
|
yarn --cwd website start
|
||||||
|
```
|
||||||
|
This command should have opened a browser window with the website; if not, open your browser and point it to `http://localhost:3000`.
|
||||||
|
|
||||||
|
If you make changes to any of the markdown files in `/docs` and want to reload the local `node` server, run:
|
||||||
|
```shell
|
||||||
|
make website/docs/README.md -B
|
||||||
|
```
|
||||||
|
|
||||||
|
You can execute the above while the node server is running and the website will be rebuilt and reloaded automatically.
|
||||||
|
|
||||||
|
## Add a New File to the Docs
|
||||||
|
|
||||||
|
If you add a new file to the `/docs` directory, you also need to create a corresponding header file containing the front-matter in `/website/docs/`.
|
||||||
|
Then, regenerate the markdown for the website with the command:
|
||||||
|
```shell
|
||||||
|
make website/docs/README.md
|
||||||
|
```
|
||||||
|
Edit `/website/sidebars.js` accordingly.
|
||||||
|
> **Note**: The `id` in the header file `/website/docs/<new file>` must match the `id` specified in `website/sidebars.js`.
|
@@ -6,14 +6,31 @@ This tool can be used to understand a mesh's topology, get the WireGuard configu
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Installing `kgctl` currently requires building the binary from source.
|
The `kgctl` binary is automatically compiled for Linux, macOS, and Windows for every release of Kilo and can be downloaded from [the GitHub releases page](https://github.com/squat/kilo/releases/latest).
|
||||||
*Note*: the [Go toolchain must be installed](https://golang.org/doc/install) in order to build the binary.
|
|
||||||
To build and install `kgctl`, run:
|
### Building from Source
|
||||||
|
Kilo is written in Golang and as a result the [Go toolchain must be installed](https://golang.org/doc/install) in order to build the `kgctl` binary.
|
||||||
|
To download the Kilo source code and then build and install `kgctl` using the latest commit all with a single command, run:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
go install github.com/squat/kilo/cmd/kgctl
|
go install github.com/squat/kilo/cmd/kgctl@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Alternatively, `kgctl` can be built and installed based on specific version of the code by specifying a Git tag or hash, e.g.:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go install github.com/squat/kilo/cmd/kgctl@0.2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
When working on Kilo locally, it can be helpful to build and test the `kgctl` binary as part of the development cycle.
|
||||||
|
In order to build a binary from a local checkout of the Git repository, run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
This will produce a `kgctl` binary at `./bin/<your-os>/<your-architecture>/kgctl`.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
|Command|Syntax|Description|
|
|Command|Syntax|Description|
|
||||||
|
37
docs/peer-validation.md
Normal file
37
docs/peer-validation.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Peer Validation
|
||||||
|
|
||||||
|
A [ValidatingAdmissionWebhook](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#validatingadmissionwebhook) can be used to avoid applying faulty Peer configurations to the cluster.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
A [ValidatingWebhookConfiguration](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#configure-admission-webhooks-on-the-fly) is a Kubernetes resource that can be used to dynamically specify a service (i.e. the webhook server) that should validate operations (e.g. `UPDATE`, `CREATE`, etc.) on a particular resource (e.g. Kilo Peers).
|
||||||
|
Once such a configuration is applied, the Kubernetes API server will send an AdmissionReviewRequest to the webhook service every time the specified operations are applied to the resource of the specified type.
|
||||||
|
With regard to the [failure policy](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#failure-policy), the API server will apply the requested changes to a resource if the request was answered with `"allowed": true`, or deny the changes if the answer was `"allowed": false`.
|
||||||
|
|
||||||
|
In case of Kilo Peer Validation, the specified operations are `UPDATE` and `CREATE`, the resources are `Peers`, and the default `failurePolicy` is set to `Fail`.
|
||||||
|
View the full ValidatingWebhookConfiguration [here](https://github.com/leonnicolas/kilo-peer-validation/blob/main/deployment-no-cabundle.yaml).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
[Kilo-Peer-Validation](https://github.com/leonnicolas/kilo-peer-validation) is a webserver that rejects any AdmissionReviewRequest with a faulty Peer configuration.
|
||||||
|
|
||||||
|
Apply the Service, the Deployment of the actual webserver, and the ValidatingWebhookConfiguration with:
|
||||||
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/leonnicolas/kilo-peer-validation/main/deployment-no-cabundle.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
The Kubernetes API server will only talk to webhook servers via TLS so the Kilo-Peer-Validation server must be given a valid TLS certificate and key, and the API server must be told what certificate authority (CA) to trust.
|
||||||
|
One way to do this is to use the [kube-webhook-certgen](https://github.com/jet/kube-webhook-certgen) project to create a Kubernetes Secret holding the TLS certificate and key for the webhook server and to make a certificate signing request to the Kubernetes API server.
|
||||||
|
The following snippet can be used to run kube-webhook-certgen in a Docker container to create a Secret and certificate signing request:
|
||||||
|
```shell
|
||||||
|
docker run -v /path/to/kubeconfig:/kubeconfig.yaml:ro jettech/kube-webhook-certgen:v1.5.2 --kubeconfig /kubeconfig.yaml create --namespace kilo --secret-name peer-validation-webhook-tls --host peer-validation,peer-validation.kilo.svc --key-name tls.key --cert-name tls.config
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, the Kubernetes API server can be told what CA to trust by patching the ValidatingWebhookConfiguration with the newly created CA bundle:
|
||||||
|
```shell
|
||||||
|
docker run -v /path/to/kubeconfig:/kubeconfig.yaml:ro jettech/kube-webhook-certgen:v1.5.2 --kubeconfig /kubeconfig.yaml patch --webhook-name peer-validation.kilo.svc --secret-name peer-validation-webhook-tls --namespace kilo --patch-mutating=false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alternative Method
|
||||||
|
|
||||||
|
An alternative method to generate a ValidatingWebhookConfiguration manifest without using Kubernetes' Certificate Signing API is described in [Kilo-Peer-Validation](https://github.com/leonnicolas/kilo-peer-validation#use-the-set-up-script).
|
@@ -6,29 +6,39 @@ This can make sense in cases where
|
|||||||
* not all nodes in a cluster have WireGuard installed; or
|
* not all nodes in a cluster have WireGuard installed; or
|
||||||
* nodes are effectively immutable and kernel modules cannot be installed.
|
* nodes are effectively immutable and kernel modules cannot be installed.
|
||||||
|
|
||||||
|
One example of a userspace implementation of WireGuard is [BoringTun].
|
||||||
|
|
||||||
## Homogeneous Clusters
|
## Homogeneous Clusters
|
||||||
|
|
||||||
In a homogeneous cluster where no node has the WireGuard kernel module, a userspace WireGuard implementation can be made available by deploying a DaemonSet.
|
In a homogeneous cluster where no node has the WireGuard kernel module, a userspace WireGuard implementation can be made available by deploying a DaemonSet.
|
||||||
This DaemonSet creates a WireGuard interface that Kilo will manage.
|
This DaemonSet creates a WireGuard interface that Kilo will manage.
|
||||||
In order to avoid race conditions, `kg` needs to be passed the `--create-interface=false` flag.
|
|
||||||
|
|
||||||
An example configuration for a k3s cluster with [boringtun](https://github.com/cloudflare/boringtun) can be applied with:
|
> **Note**: in order to avoid race conditions, `kg` needs to be passed the `--create-interface=false` flag.
|
||||||
|
|
||||||
|
An example configuration for a K3s cluster with [BoringTun] can be applied with:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/crds.yaml
|
||||||
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-k3s-userspace.yaml
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-k3s-userspace.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
__Note:__ even if some nodes have the WireGuard kernel module, this configuration will cause all nodes to use the userspace implementation of WireGuard.
|
> **Note**: even if some nodes have the WireGuard kernel module, this configuration will cause all nodes to use the userspace implementation of WireGuard.
|
||||||
|
|
||||||
## Heterogeneous Clusters
|
## Heterogeneous Clusters
|
||||||
|
|
||||||
In a heterogeneous cluster where some nodes are missing the WireGuard kernel module, a userspace WireGuard implementation can be provided only to the nodes that need it while enabling the other nodes to leverage WireGuard via the kernel module.
|
In a heterogeneous cluster where some nodes are missing the WireGuard kernel module, a userspace WireGuard implementation can be provided only to the nodes that need it while enabling the other nodes to leverage WireGuard via the kernel module.
|
||||||
An example of such a configuration for a k3s cluster can by applied with:
|
An example of such a configuration for a K3s cluster can by applied with:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/crds.yaml
|
||||||
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-k3s-userspace-heterogeneous.yaml
|
kubectl apply -f https://raw.githubusercontent.com/squat/kilo/main/manifests/kilo-k3s-userspace-heterogeneous.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
This configuration will deploy [nkml](https://github.com/leonnicolas/nkml) as a DaemonSet to label all nodes according to the presence of the WireGuard kernel module.
|
This configuration will deploy [nkml](https://github.com/leonnicolas/nkml) as a DaemonSet to label all nodes according to the presence of the WireGuard kernel module.
|
||||||
It will also create two different DaemonSets with Kilo: `kilo` without userspace WireGuard and `kilo-userspace` with boringtun as a sidecar.
|
It will also create two different DaemonSets with Kilo:
|
||||||
__Note:__ because Kilo is dependant on nkml, nkml must be run on the host network before CNI is available and requires a kubeconfig in order to access the Kubernetes API.
|
1. `kilo` without userspace WireGuard; and
|
||||||
|
1. `kilo-userspace` with [BoringTun] as a sidecar.
|
||||||
|
|
||||||
|
> **Note**: because Kilo is dependant on nkml, nkml must be run on the host network before CNI is available and requires a kubeconfig in order to access the Kubernetes API.
|
||||||
|
|
||||||
|
[BoringTun]: https://github.com/cloudflare/boringtun
|
||||||
|
43
e2e/full-mesh.sh
Normal file
43
e2e/full-mesh.sh
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. lib.sh
|
||||||
|
|
||||||
|
setup_suite() {
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
_kubectl patch ds -n kube-system kilo -p '{"spec": {"template":{"spec":{"containers":[{"name":"kilo","args":["--hostname=$(NODE_NAME)","--create-interface=false","--kubeconfig=/etc/kubernetes/kubeconfig","--mesh-granularity=full"]}]}}}}'
|
||||||
|
block_until_ready_by_name kube-system kilo-userspace
|
||||||
|
_kubectl wait pod -l app.kubernetes.io/name=adjacency --for=condition=Ready --timeout 3m
|
||||||
|
}
|
||||||
|
|
||||||
|
test_full_mesh_connectivity() {
|
||||||
|
assert "retry 30 5 '' check_ping" "should be able to ping all Pods"
|
||||||
|
assert "retry 10 5 'the adjacency matrix is not complete yet' check_adjacent 3" "adjacency should return the right number of successful pings"
|
||||||
|
echo "sleep for 30s (one reconciliation period) and try again..."
|
||||||
|
sleep 30
|
||||||
|
assert "retry 10 5 'the adjacency matrix is not complete yet' check_adjacent 3" "adjacency should return the right number of successful pings after reconciling"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_full_mesh_peer() {
|
||||||
|
check_peer wg1 e2e 10.5.0.1/32 full
|
||||||
|
}
|
||||||
|
|
||||||
|
test_full_mesh_allowed_location_ips() {
|
||||||
|
docker exec kind-cluster-kilo-control-plane ip address add 10.6.0.1/32 dev eth0
|
||||||
|
_kubectl annotate node kind-cluster-kilo-control-plane kilo.squat.ai/allowed-location-ips=10.6.0.1/32
|
||||||
|
assert_equals Unauthorized "$(retry 10 5 'IP is not yet routable' curl_pod -m 1 -s -k https://10.6.0.1:10250/healthz)" "should be able to make HTTP request to allowed location IP"
|
||||||
|
_kubectl annotate node kind-cluster-kilo-control-plane kilo.squat.ai/allowed-location-ips-
|
||||||
|
assert "retry 10 5 'IP is still routable' _not curl_pod -m 1 -s -k https://10.6.0.1:10250/healthz" "should not be able to make HTTP request to allowed location IP"
|
||||||
|
docker exec kind-cluster-kilo-control-plane ip address delete 10.6.0.1/32 dev eth0
|
||||||
|
}
|
||||||
|
|
||||||
|
test_reject_peer_empty_allowed_ips() {
|
||||||
|
assert_fail "create_peer e2e '' 0 foo" "should not be able to create Peer with empty allowed IPs"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_reject_peer_empty_public_key() {
|
||||||
|
assert_fail "create_peer e2e 10.5.0.1/32 0 ''" "should not be able to create Peer with empty public key"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_mesh_granularity_auto_detect() {
|
||||||
|
assert_equals "$(_kgctl graph)" "$(_kgctl graph --mesh-granularity full)"
|
||||||
|
}
|
25
e2e/helper-curl.yaml
Normal file
25
e2e/helper-curl.yaml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: curl
|
||||||
|
name: curl
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: curl
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: curl
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- while [ 1 -eq 1 ] ; do sleep 10; done
|
||||||
|
image: curlimages/curl
|
||||||
|
name: curl
|
||||||
|
restartPolicy: Always
|
204
e2e/kilo-kind-userspace.yaml
Normal file
204
e2e/kilo-kind-userspace.yaml
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kilo
|
||||||
|
data:
|
||||||
|
cni-conf.json: |
|
||||||
|
{
|
||||||
|
"cniVersion":"0.3.1",
|
||||||
|
"name":"kilo",
|
||||||
|
"plugins":[
|
||||||
|
{
|
||||||
|
"name":"kubernetes",
|
||||||
|
"type":"bridge",
|
||||||
|
"bridge":"kube-bridge",
|
||||||
|
"isDefaultGateway":true,
|
||||||
|
"forceAddress":true,
|
||||||
|
"mtu": 1420,
|
||||||
|
"ipam":{
|
||||||
|
"type":"host-local"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type":"portmap",
|
||||||
|
"snat":true,
|
||||||
|
"capabilities":{
|
||||||
|
"portMappings":true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- nodes
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- kilo.squat.ai
|
||||||
|
resources:
|
||||||
|
- peers
|
||||||
|
verbs:
|
||||||
|
- list
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- apiextensions.k8s.io
|
||||||
|
resources:
|
||||||
|
- customresourcedefinitions
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: kilo
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: DaemonSet
|
||||||
|
metadata:
|
||||||
|
name: kilo
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kilo-userspace
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: kilo-userspace
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kilo-userspace
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
spec:
|
||||||
|
serviceAccountName: kilo
|
||||||
|
hostNetwork: true
|
||||||
|
containers:
|
||||||
|
- name: kilo
|
||||||
|
image: squat/kilo:test
|
||||||
|
imagePullPolicy: Never
|
||||||
|
args:
|
||||||
|
- --hostname=$(NODE_NAME)
|
||||||
|
- --create-interface=false
|
||||||
|
- --mesh-granularity=full
|
||||||
|
- --kubeconfig=/etc/kubernetes/kubeconfig
|
||||||
|
env:
|
||||||
|
- name: NODE_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: cni-conf-dir
|
||||||
|
mountPath: /etc/cni/net.d
|
||||||
|
- name: kilo-dir
|
||||||
|
mountPath: /var/lib/kilo
|
||||||
|
- name: lib-modules
|
||||||
|
mountPath: /lib/modules
|
||||||
|
readOnly: true
|
||||||
|
- name: xtables-lock
|
||||||
|
mountPath: /run/xtables.lock
|
||||||
|
readOnly: false
|
||||||
|
- name: wireguard
|
||||||
|
mountPath: /var/run/wireguard
|
||||||
|
readOnly: false
|
||||||
|
- name: kubeconfig
|
||||||
|
mountPath: /etc/kubernetes
|
||||||
|
readOnly: true
|
||||||
|
- name: boringtun
|
||||||
|
image: leonnicolas/boringtun:alpine
|
||||||
|
args:
|
||||||
|
- --disable-drop-privileges=true
|
||||||
|
- --foreground
|
||||||
|
- kilo0
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
volumeMounts:
|
||||||
|
- name: wireguard
|
||||||
|
mountPath: /var/run/wireguard
|
||||||
|
readOnly: false
|
||||||
|
initContainers:
|
||||||
|
- name: install-cni
|
||||||
|
image: squat/kilo:test
|
||||||
|
imagePullPolicy: Never
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- set -e -x;
|
||||||
|
cp /opt/cni/bin/* /host/opt/cni/bin/;
|
||||||
|
TMP_CONF="$CNI_CONF_NAME".tmp;
|
||||||
|
echo "$CNI_NETWORK_CONFIG" > $TMP_CONF;
|
||||||
|
rm -f /host/etc/cni/net.d/*;
|
||||||
|
mv $TMP_CONF /host/etc/cni/net.d/$CNI_CONF_NAME
|
||||||
|
env:
|
||||||
|
- name: CNI_CONF_NAME
|
||||||
|
value: 10-kilo.conflist
|
||||||
|
- name: CNI_NETWORK_CONFIG
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: kilo
|
||||||
|
key: cni-conf.json
|
||||||
|
volumeMounts:
|
||||||
|
- name: cni-bin-dir
|
||||||
|
mountPath: /host/opt/cni/bin
|
||||||
|
- name: cni-conf-dir
|
||||||
|
mountPath: /host/etc/cni/net.d
|
||||||
|
tolerations:
|
||||||
|
- effect: NoSchedule
|
||||||
|
operator: Exists
|
||||||
|
- effect: NoExecute
|
||||||
|
operator: Exists
|
||||||
|
volumes:
|
||||||
|
- name: cni-bin-dir
|
||||||
|
hostPath:
|
||||||
|
path: /opt/cni/bin
|
||||||
|
- name: cni-conf-dir
|
||||||
|
hostPath:
|
||||||
|
path: /etc/cni/net.d
|
||||||
|
- name: kilo-dir
|
||||||
|
hostPath:
|
||||||
|
path: /var/lib/kilo
|
||||||
|
- name: lib-modules
|
||||||
|
hostPath:
|
||||||
|
path: /lib/modules
|
||||||
|
- name: xtables-lock
|
||||||
|
hostPath:
|
||||||
|
path: /run/xtables.lock
|
||||||
|
type: FileOrCreate
|
||||||
|
- name: wireguard
|
||||||
|
hostPath:
|
||||||
|
path: /var/run/wireguard
|
||||||
|
- name: kubeconfig
|
||||||
|
secret:
|
||||||
|
secretName: kubeconfig
|
10
e2e/kind-config.yaml
Normal file
10
e2e/kind-config.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
kind: Cluster
|
||||||
|
apiVersion: kind.x-k8s.io/v1alpha4
|
||||||
|
nodes:
|
||||||
|
- role: control-plane$WORKERS
|
||||||
|
networking:
|
||||||
|
disableDefaultCNI: true # disable kindnet
|
||||||
|
apiServerAddress: 172.18.0.1
|
||||||
|
apiServerPort: $API_SERVER_PORT
|
||||||
|
podSubnet: $POD_SUBNET
|
||||||
|
serviceSubnet: $SERVICE_SUBNET
|
200
e2e/lib.sh
Executable file
200
e2e/lib.sh
Executable file
@@ -0,0 +1,200 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
KUBECONFIG="kind.yaml"
|
||||||
|
KIND_CLUSTER="kind-cluster-kilo"
|
||||||
|
KIND_BINARY="${KIND_BINARY:-kind}"
|
||||||
|
KUBECTL_BINARY="${KUBECTL_BINARY:-kubectl}"
|
||||||
|
KGCTL_BINARY="${KGCTL_BINARY:-kgctl}"
|
||||||
|
KILO_IMAGE="${KILO_IMAGE:-squat/kilo}"
|
||||||
|
|
||||||
|
retry() {
|
||||||
|
local COUNT="${1:-10}"
|
||||||
|
local SLEEP="${2:-5}"
|
||||||
|
local ERROR=$3
|
||||||
|
[ -n "$ERROR" ] && ERROR="$ERROR "
|
||||||
|
shift 3
|
||||||
|
for c in $(seq 1 "$COUNT"); do
|
||||||
|
if "$@"; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
printf "%s(attempt %d/%d)\n" "$ERROR" "$c" "$COUNT" | color "$YELLOW" 1>&2
|
||||||
|
if [ "$c" != "$COUNT" ]; then
|
||||||
|
printf "retrying in %d seconds...\n" "$SLEEP" | color "$YELLOW" 1>&2
|
||||||
|
sleep "$SLEEP"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_not() {
|
||||||
|
if "$@"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# _kubectl is a helper that calls kubectl with the --kubeconfig flag.
|
||||||
|
_kubectl() {
|
||||||
|
$KUBECTL_BINARY --kubeconfig="$KUBECONFIG" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# _kgctl is a helper that calls kgctl with the --kubeconfig flag.
|
||||||
|
_kgctl() {
|
||||||
|
$KGCTL_BINARY --kubeconfig="$KUBECONFIG" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# _kind is a helper that calls kind with the --kubeconfig flag.
|
||||||
|
_kind() {
|
||||||
|
$KIND_BINARY --kubeconfig="$KUBECONFIG" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC2120
|
||||||
|
build_kind_config() {
|
||||||
|
local WORKER_COUNT="${1:-0}"
|
||||||
|
export API_SERVER_PORT="${2:-6443}"
|
||||||
|
export POD_SUBNET="${3:-10.42.0.0/16}"
|
||||||
|
export SERVICE_SUBNET="${4:-10.43.0.0/16}"
|
||||||
|
export WORKERS=""
|
||||||
|
local i=0
|
||||||
|
while [ "$i" -lt "$WORKER_COUNT" ]; do
|
||||||
|
WORKERS="$(printf "%s\n- role: worker" "$WORKERS")"
|
||||||
|
((i++))
|
||||||
|
done
|
||||||
|
envsubst < ./kind-config.yaml
|
||||||
|
unset API_SERVER_PORT POD_SUBNET SERVICE_SUBNET WORKERS
|
||||||
|
}
|
||||||
|
|
||||||
|
create_interface() {
|
||||||
|
docker run -d --name="$1" --rm --network=host --cap-add=NET_ADMIN --device=/dev/net/tun -v /var/run/wireguard:/var/run/wireguard -e WG_LOG_LEVEL=debug leonnicolas/boringtun --foreground --disable-drop-privileges true "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_interface() {
|
||||||
|
docker rm --force "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_peer() {
|
||||||
|
cat <<EOF | _kubectl apply -f -
|
||||||
|
apiVersion: kilo.squat.ai/v1alpha1
|
||||||
|
kind: Peer
|
||||||
|
metadata:
|
||||||
|
name: $1
|
||||||
|
spec:
|
||||||
|
allowedIPs:
|
||||||
|
- $2
|
||||||
|
persistentKeepalive: $3
|
||||||
|
publicKey: $4
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_peer() {
|
||||||
|
_kubectl delete peer "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_ready() {
|
||||||
|
for pod in $(_kubectl -n "$1" get pods -o name -l "$2"); do
|
||||||
|
if ! _kubectl -n "$1" get "$pod" | tail -n 1 | grep -q Running; then
|
||||||
|
return 1;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Returns non zero if one pod of the given name in the given namespace is not ready.
|
||||||
|
block_until_ready_by_name() {
|
||||||
|
block_until_ready "$1" "app.kubernetes.io/name=$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Blocks until all pods of a deployment are ready.
|
||||||
|
block_until_ready() {
|
||||||
|
retry 30 5 "some $2 pods are not ready yet" is_ready "$1" "$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# create_cluster launches a kind cluster and deploys Kilo, Adjacency, and a helper with curl.
|
||||||
|
create_cluster() {
|
||||||
|
# shellcheck disable=SC2119
|
||||||
|
local CONFIG="${1:-$(build_kind_config)}"
|
||||||
|
_kind delete clusters $KIND_CLUSTER > /dev/null
|
||||||
|
# Create the kind cluster.
|
||||||
|
_kind create cluster --name $KIND_CLUSTER --config <(echo "$CONFIG")
|
||||||
|
# Load the Kilo image into kind.
|
||||||
|
docker tag "$KILO_IMAGE" squat/kilo:test
|
||||||
|
# This command does not accept the --kubeconfig flag, so call the command directly.
|
||||||
|
$KIND_BINARY load docker-image squat/kilo:test --name $KIND_CLUSTER
|
||||||
|
# Create the kubeconfig secret.
|
||||||
|
_kubectl create secret generic kubeconfig --from-file=kubeconfig="$KUBECONFIG" -n kube-system
|
||||||
|
# Apply Kilo the the cluster.
|
||||||
|
_kubectl apply -f ../manifests/crds.yaml
|
||||||
|
_kubectl apply -f kilo-kind-userspace.yaml
|
||||||
|
block_until_ready_by_name kube-system kilo-userspace
|
||||||
|
_kubectl wait nodes --all --for=condition=Ready
|
||||||
|
# Wait for CoreDNS.
|
||||||
|
block_until_ready kube_system k8s-app=kube-dns
|
||||||
|
# Ensure the curl helper is not scheduled on a control-plane node.
|
||||||
|
_kubectl apply -f helper-curl.yaml
|
||||||
|
block_until_ready_by_name default curl
|
||||||
|
_kubectl taint node $KIND_CLUSTER-control-plane node-role.kubernetes.io/master:NoSchedule-
|
||||||
|
_kubectl apply -f https://raw.githubusercontent.com/heptoprint/adjacency/master/example.yaml
|
||||||
|
block_until_ready_by_name adjacency adjacency
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_cluster () {
|
||||||
|
_kind delete clusters $KIND_CLUSTER
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_pod() {
|
||||||
|
_kubectl get pods -l app.kubernetes.io/name=curl -o name | xargs -I{} "$KUBECTL_BINARY" --kubeconfig="$KUBECONFIG" exec {} -- /usr/bin/curl "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ping() {
|
||||||
|
local LOCAL
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case $1 in
|
||||||
|
--local)
|
||||||
|
LOCAL=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
for ip in $(_kubectl get pods -l app.kubernetes.io/name=adjacency -o jsonpath='{.items[*].status.podIP}'); do
|
||||||
|
if [ -n "$LOCAL" ]; then
|
||||||
|
ping=$(curl -m 1 -s http://"$ip":8080/ping)
|
||||||
|
else
|
||||||
|
ping=$(curl_pod -m 1 -s http://"$ip":8080/ping)
|
||||||
|
fi
|
||||||
|
if [ "$ping" = "pong" ]; then
|
||||||
|
echo "successfully pinged $ip"
|
||||||
|
else
|
||||||
|
printf 'failed to ping %s; expected "pong" but got "%s"\n' "$ip" "$ping"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
check_adjacent() {
|
||||||
|
curl_pod adjacency:8080/?format=fancy
|
||||||
|
[ "$(curl_pod -m 1 -s adjacency:8080/?format=json | jq '.[].latencies[].ok' | grep -c true)" -eq $(($1*$1)) ]
|
||||||
|
}
|
||||||
|
|
||||||
|
check_peer() {
|
||||||
|
local INTERFACE=$1
|
||||||
|
local PEER=$2
|
||||||
|
local ALLOWED_IP=$3
|
||||||
|
local GRANULARITY=$4
|
||||||
|
create_interface "$INTERFACE"
|
||||||
|
docker run --rm --entrypoint=/usr/bin/wg "$KILO_IMAGE" genkey > "$INTERFACE"
|
||||||
|
assert "create_peer $PEER $ALLOWED_IP 10 $(docker run --rm --entrypoint=/bin/sh -v "$PWD/$INTERFACE":/key "$KILO_IMAGE" -c 'cat /key | wg pubkey')" "should be able to create Peer"
|
||||||
|
assert "_kgctl showconf peer $PEER --mesh-granularity=$GRANULARITY > $PEER.ini" "should be able to get Peer configuration"
|
||||||
|
assert "docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/usr/bin/wg -v /var/run/wireguard:/var/run/wireguard -v $PWD/$PEER.ini:/peer.ini $KILO_IMAGE setconf $INTERFACE /peer.ini" "should be able to apply configuration from kgctl"
|
||||||
|
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/usr/bin/wg -v /var/run/wireguard:/var/run/wireguard -v "$PWD/$INTERFACE":/key "$KILO_IMAGE" set "$INTERFACE" private-key /key
|
||||||
|
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip "$KILO_IMAGE" address add "$ALLOWED_IP" dev "$INTERFACE"
|
||||||
|
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip "$KILO_IMAGE" link set "$INTERFACE" up
|
||||||
|
docker run --rm --network=host --cap-add=NET_ADMIN --entrypoint=/sbin/ip "$KILO_IMAGE" route add 10.42/16 dev "$INTERFACE"
|
||||||
|
assert "retry 10 5 '' check_ping --local" "should be able to ping Pods from host"
|
||||||
|
assert_equals "$(_kgctl showconf peer "$PEER")" "$(_kgctl showconf peer "$PEER" --mesh-granularity="$GRANULARITY")" "kgctl should be able to auto detect the mesh granularity"
|
||||||
|
rm "$INTERFACE" "$PEER".ini
|
||||||
|
delete_peer "$PEER"
|
||||||
|
delete_interface "$INTERFACE"
|
||||||
|
}
|
26
e2e/location-mesh.sh
Executable file
26
e2e/location-mesh.sh
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. lib.sh
|
||||||
|
|
||||||
|
setup_suite() {
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
_kubectl patch ds -n kube-system kilo -p '{"spec": {"template":{"spec":{"containers":[{"name":"kilo","args":["--hostname=$(NODE_NAME)","--create-interface=false","--kubeconfig=/etc/kubernetes/kubeconfig","--mesh-granularity=location"]}]}}}}'
|
||||||
|
block_until_ready_by_name kube-system kilo-userspace
|
||||||
|
_kubectl wait pod -l app.kubernetes.io/name=adjacency --for=condition=Ready --timeout 3m
|
||||||
|
}
|
||||||
|
|
||||||
|
test_location_mesh_connectivity() {
|
||||||
|
assert "retry 30 5 '' check_ping" "should be able to ping all Pods"
|
||||||
|
assert "retry 10 5 'the adjacency matrix is not complete yet' check_adjacent 3" "adjacency should return the right number of successful pings"
|
||||||
|
echo "sleep for 30s (one reconciliation period) and try again..."
|
||||||
|
sleep 30
|
||||||
|
assert "retry 10 5 'the adjacency matrix is not complete yet' check_adjacent 3" "adjacency should return the right number of successful pings after reconciling"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_location_mesh_peer() {
|
||||||
|
check_peer wg1 e2e 10.5.0.1/32 location
|
||||||
|
}
|
||||||
|
|
||||||
|
test_mesh_granularity_auto_detect() {
|
||||||
|
assert_equals "$(_kgctl graph)" "$(_kgctl graph --mesh-granularity location)"
|
||||||
|
}
|
66
e2e/multi-cluster.sh
Executable file
66
e2e/multi-cluster.sh
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. lib.sh
|
||||||
|
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
KUBECONFIG2="$KUBECONFIG"2
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
KIND_CLUSTER2="$KIND_CLUSTER"2
|
||||||
|
|
||||||
|
setup_suite() {
|
||||||
|
KUBECONFIG=$KUBECONFIG2 KIND_CLUSTER=$KIND_CLUSTER2 create_cluster "$(build_kind_config 1 6444 10.44.0.0/16 10.45.0.0/16)"
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
KUBECONFIG=$KUBECONFIG2 _kubectl patch ds -n kube-system kilo -p '{"spec": {"template":{"spec":{"containers":[{"name":"kilo","args":["--hostname=$(NODE_NAME)","--create-interface=false","--kubeconfig=/etc/kubernetes/kubeconfig","--mesh-granularity=full","--subnet=10.6.0.0/16"]}]}}}}'
|
||||||
|
KUBECONFIG=$KUBECONFIG2 block_until_ready_by_name kube-system kilo-userspace
|
||||||
|
# Register the nodes in cluster1 as peers of cluster2.
|
||||||
|
for n in $(_kubectl get no -o name | cut -d'/' -f2); do
|
||||||
|
# Specify the service CIDR as an extra IP range that should be routable.
|
||||||
|
$KGCTL_BINARY --kubeconfig "$KUBECONFIG" showconf node "$n" --as-peer -o yaml --allowed-ips 10.43.0.0/16 | $KUBECTL_BINARY --kubeconfig "$KUBECONFIG2" apply -f -
|
||||||
|
done
|
||||||
|
# Register the nodes in cluster2 as peers of cluster1.
|
||||||
|
for n in $(KUBECONFIG=$KUBECONFIG2 _kubectl get no -o name | cut -d'/' -f2); do
|
||||||
|
# Specify the service CIDR as an extra IP range that should be routable.
|
||||||
|
$KGCTL_BINARY --kubeconfig "$KUBECONFIG2" showconf node "$n" --as-peer -o yaml --allowed-ips 10.45.0.0/16 | $KUBECTL_BINARY --kubeconfig "$KUBECONFIG" apply -f -
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
test_multi_cluster_pod_connectivity() {
|
||||||
|
for ip in $(KUBECONFIG=$KUBECONFIG2 _kubectl get pods -l app.kubernetes.io/name=adjacency -o jsonpath='{.items[*].status.podIP}'); do
|
||||||
|
assert_equals pong "$(retry 10 5 "$ip is not yet routable" curl_pod -m 1 -s http://"$ip":8080/ping)" "should be able to make HTTP request from cluster 1 to Pod in cluster 2"
|
||||||
|
done
|
||||||
|
for ip in $(_kubectl get pods -l app.kubernetes.io/name=adjacency -o jsonpath='{.items[*].status.podIP}'); do
|
||||||
|
assert_equals pong "$(KUBECONFIG="$KUBECONFIG2" retry 10 5 "$ip is not yet routable" curl_pod -m 1 -s http://"$ip":8080/ping)" "should be able to make HTTP request from cluster 2 to Pod in cluster 1"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
test_multi_cluster_service_connectivity() {
|
||||||
|
# Mirror the Kubernetes API service from cluster1 into cluster2.
|
||||||
|
cat <<EOF | $KUBECTL_BINARY --kubeconfig "$KUBECONFIG2" apply -f -
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: mirrored-kubernetes
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 443
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Endpoints
|
||||||
|
metadata:
|
||||||
|
name: mirrored-kubernetes
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: $(_kubectl get service kubernetes -o jsonpath='{.spec.clusterIP}') # The cluster IP of the Kubernetes API service on cluster1.
|
||||||
|
ports:
|
||||||
|
- port: 443
|
||||||
|
EOF
|
||||||
|
assert_equals ok "$(KUBECONFIG="$KUBECONFIG2" retry 10 5 "service is not yet routable" curl_pod -m 1 -s -k https://mirrored-kubernetes/readyz)" "should be able to make HTTP request from cluster 2 to service in cluster 1"
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_suite () {
|
||||||
|
# Remove the nodes in cluster2 as peers of cluster1.
|
||||||
|
for n in $(KUBECONFIG=$KUBECONFIG2 _kubectl get no -o name | cut -d'/' -f2); do
|
||||||
|
_kubectl delete peer "$n"
|
||||||
|
done
|
||||||
|
KUBECONFIG=$KUBECONFIG2 KIND_CLUSTER=$KIND_CLUSTER2 delete_cluster
|
||||||
|
}
|
7
e2e/setup.sh
Normal file
7
e2e/setup.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. lib.sh
|
||||||
|
|
||||||
|
setup_suite() {
|
||||||
|
create_cluster "$(build_kind_config 2)"
|
||||||
|
}
|
7
e2e/teardown.sh
Normal file
7
e2e/teardown.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. lib.sh
|
||||||
|
|
||||||
|
teardown_suite () {
|
||||||
|
delete_cluster
|
||||||
|
}
|
62
go.mod
62
go.mod
@@ -3,64 +3,26 @@ module github.com/squat/kilo
|
|||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
|
||||||
github.com/ant31/crd-validation v0.0.0-20180801212718-38f6a293f140
|
|
||||||
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310
|
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310
|
||||||
github.com/campoy/embedmd v1.0.0
|
github.com/campoy/embedmd v1.0.0
|
||||||
github.com/containernetworking/cni v0.6.0
|
github.com/containernetworking/cni v0.6.0
|
||||||
github.com/containernetworking/plugins v0.6.0
|
github.com/containernetworking/plugins v0.6.0
|
||||||
github.com/coreos/go-iptables v0.4.0
|
github.com/coreos/go-iptables v0.4.0
|
||||||
github.com/emicklei/go-restful v2.9.3+incompatible // indirect
|
github.com/go-kit/kit v0.9.0
|
||||||
github.com/evanphx/json-patch v4.2.0+incompatible // indirect
|
|
||||||
github.com/ghodss/yaml v1.0.0 // indirect
|
|
||||||
github.com/go-kit/kit v0.8.0
|
|
||||||
github.com/go-logfmt/logfmt v0.4.0 // indirect
|
|
||||||
github.com/go-openapi/jsonpointer v0.19.0 // indirect
|
|
||||||
github.com/go-openapi/jsonreference v0.19.0 // indirect
|
|
||||||
github.com/go-openapi/spec v0.19.0
|
|
||||||
github.com/go-openapi/swag v0.19.0 // indirect
|
|
||||||
github.com/go-stack/stack v1.8.0 // indirect
|
|
||||||
github.com/gogo/protobuf v1.2.1 // indirect
|
|
||||||
github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff // indirect
|
|
||||||
github.com/golang/protobuf v1.3.1 // indirect
|
|
||||||
github.com/google/gofuzz v1.0.0 // indirect
|
|
||||||
github.com/googleapis/gnostic v0.2.0 // indirect
|
|
||||||
github.com/hashicorp/golang-lru v0.5.0 // indirect
|
|
||||||
github.com/imdario/mergo v0.3.6 // indirect
|
github.com/imdario/mergo v0.3.6 // indirect
|
||||||
github.com/json-iterator/go v1.1.6 // indirect
|
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
|
||||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348
|
||||||
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
|
||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
|
||||||
github.com/oklog/run v1.0.0
|
github.com/oklog/run v1.0.0
|
||||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
github.com/prometheus/client_golang v1.7.1
|
||||||
github.com/onsi/gomega v1.5.0 // indirect
|
github.com/spf13/cobra v1.1.3
|
||||||
github.com/prometheus/client_golang v0.9.2
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/spf13/cobra v0.0.4-0.20190321000552-67fc4837d267
|
|
||||||
github.com/spf13/pflag v1.0.3
|
|
||||||
github.com/stretchr/testify v1.3.0 // indirect
|
|
||||||
github.com/vishvananda/netlink v1.0.0
|
github.com/vishvananda/netlink v1.0.0
|
||||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
|
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
|
||||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 // indirect
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09 // indirect
|
k8s.io/api v0.21.1
|
||||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect
|
k8s.io/apiextensions-apiserver v0.21.1
|
||||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872
|
k8s.io/apimachinery v0.21.1
|
||||||
golang.org/x/text v0.3.2 // indirect
|
k8s.io/client-go v0.21.1
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect
|
k8s.io/code-generator v0.21.1
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 // indirect
|
sigs.k8s.io/controller-tools v0.6.0
|
||||||
google.golang.org/appengine v1.5.0 // indirect
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
|
||||||
k8s.io/api v0.0.0-20190313235455-40a48860b5ab
|
|
||||||
k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed
|
|
||||||
k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1
|
|
||||||
k8s.io/client-go v11.0.0+incompatible
|
|
||||||
k8s.io/code-generator v0.0.0-20190311093542-50b561225d70
|
|
||||||
k8s.io/gengo v0.0.0-20181106084056-51747d6e00da // indirect
|
|
||||||
k8s.io/klog v0.3.0 // indirect
|
|
||||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30
|
|
||||||
k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 // indirect
|
|
||||||
sigs.k8s.io/yaml v1.1.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
732
go.sum
732
go.sum
@@ -1,207 +1,737 @@
|
|||||||
|
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.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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||||
|
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/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
|
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
|
||||||
|
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.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/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
|
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||||
|
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/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/ant31/crd-validation v0.0.0-20180801212718-38f6a293f140 h1:ljF9TLBK+JYXKB8aklLhcubPDBUjpvzWKiqgZ37s+QY=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/ant31/crd-validation v0.0.0-20180801212718-38f6a293f140/go.mod h1:X0noFIik9YqfhGYBLEHg8LJKEwy7QIitLQuFMpKLcPk=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
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/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310 h1:t+qxRrRtwNiUYA+Xh2jSXhoG2grnMCMKX4Fg6lx9X1U=
|
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310 h1:t+qxRrRtwNiUYA+Xh2jSXhoG2grnMCMKX4Fg6lx9X1U=
|
||||||
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=
|
github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
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/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||||
|
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
|
github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
|
||||||
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
|
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||||
github.com/containernetworking/cni v0.6.0 h1:FXICGBZNMtdHlW65trpoHviHctQD3seWhRRcqp2hMOU=
|
github.com/containernetworking/cni v0.6.0 h1:FXICGBZNMtdHlW65trpoHviHctQD3seWhRRcqp2hMOU=
|
||||||
github.com/containernetworking/cni v0.6.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
github.com/containernetworking/cni v0.6.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||||
github.com/containernetworking/plugins v0.6.0 h1:bqPT7yYisnWs+FrtgY5/qLEB9QZ/6z11wMNCwSdzZm0=
|
github.com/containernetworking/plugins v0.6.0 h1:bqPT7yYisnWs+FrtgY5/qLEB9QZ/6z11wMNCwSdzZm0=
|
||||||
github.com/containernetworking/plugins v0.6.0/go.mod h1:dagHaAhNjXjT9QYOklkKJDGaQPTg4pf//FrUcJeb7FU=
|
github.com/containernetworking/plugins v0.6.0/go.mod h1:dagHaAhNjXjT9QYOklkKJDGaQPTg4pf//FrUcJeb7FU=
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-iptables v0.4.0 h1:wh4UbVs8DhLUbpyq97GLJDKrQMjEDD63T1xE4CrsKzQ=
|
github.com/coreos/go-iptables v0.4.0 h1:wh4UbVs8DhLUbpyq97GLJDKrQMjEDD63T1xE4CrsKzQ=
|
||||||
github.com/coreos/go-iptables v0.4.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
github.com/coreos/go-iptables v0.4.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.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
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/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/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.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/emicklei/go-restful v2.9.3+incompatible h1:2OwhVdhtzYUp5P5wuGsVDPagKSRd9JK72sJCHVCXh5g=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/emicklei/go-restful v2.9.3+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
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.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
|
||||||
|
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/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||||
|
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
|
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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
|
||||||
|
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 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
github.com/go-openapi/jsonpointer v0.19.0 h1:FTUMcX77w5rQkClIzDtTxvn6Bsa894CcrzNj2MMfeg8=
|
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||||
github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
||||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||||
github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=
|
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||||
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
|
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||||
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||||
github.com/go-openapi/swag v0.19.0 h1:Kg7Wl7LkTPlmc393QZQ/5rQadPhi7pBVEMZxyTi0Ii8=
|
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||||
github.com/go-openapi/swag v0.19.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
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 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/gobuffalo/flect v0.2.2 h1:PAVD7sp0KOdfswjAw9BpLCU9hXo7wFSzgpQ+zNeks/A=
|
||||||
|
github.com/gobuffalo/flect v0.2.2/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
|
||||||
|
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.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff h1:kOkM9whyQYodu09SJ6W3NCsHG7crFaJILQ22Gozp3lg=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
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.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
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.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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
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.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
|
github.com/google/go-cmp v0.5.6/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.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
|
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
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 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||||
|
github.com/google/uuid v1.1.2/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 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I=
|
||||||
|
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/websocket v0.0.0-20170926233335-4201258b820c/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/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||||
|
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||||
|
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/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
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.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
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.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 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
|
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.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
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/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983 h1:wL11wNW7dhKIcRCHSm4sHKPWz0tt4mwBsVodG7+Xyqg=
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
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 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||||
|
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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/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 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
|
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-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.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||||
|
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||||
|
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.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
||||||
|
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||||
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
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.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
|
||||||
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
|
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
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.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
||||||
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
|
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.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
|
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||||
|
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
|
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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/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.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/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cobra v0.0.4-0.20190321000552-67fc4837d267 h1:I9j1PLS64+NgCtkgbomGInboj1NFH1KF1tkVKlt3yF4=
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v0.0.4-0.20190321000552-67fc4837d267/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||||
|
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
|
||||||
|
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/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.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||||
github.com/stretchr/objx v0.1.0/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/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
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 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
|
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/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM=
|
github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM=
|
||||||
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4=
|
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4=
|
||||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
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.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/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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI=
|
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/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-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
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-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 h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||||
|
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/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||||
|
golang.org/x/mod v0.4.2/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-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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-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-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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09 h1:KaQtG+aDELoNmXYas3TVkGNYRuq8JQ1aa7LJt8EXVyo=
|
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
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-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-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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
|
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 h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||||
|
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-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-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-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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872 h1:cGjJzUd8RgBw428LXP65YXni0aiGNA4Bl+ls8SmLOm8=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/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-20190606165138-5da285871e9c/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-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-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-20191026070338-33540a1f6037/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-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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/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-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-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
|
||||||
|
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
|
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-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-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-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-20180917221912-90fa682c2a6e/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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg=
|
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-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-20191112195655-aa38f8e97acc/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-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
|
||||||
|
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
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.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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
|
||||||
google.golang.org/appengine v1.5.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 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
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-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-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/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
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.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
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/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/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/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.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
k8s.io/api v0.0.0-20190313235455-40a48860b5ab h1:DG9A67baNpoeweOy2spF1OWHhnVY5KR7/Ek/+U1lVZc=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
k8s.io/api v0.0.0-20190313235455-40a48860b5ab/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed h1:rCteec//ELIjZMfjIGQbVtZooyaofqDJwsmWwWKItNs=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
k8s.io/apiextensions-apiserver v0.0.0-20190315093550-53c4693659ed/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1 h1:IS7K02iBkQXpCeieSiyJjGoLSdVOv2DbPaWHJ+ZtgKg=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
k8s.io/code-generator v0.0.0-20190311093542-50b561225d70 h1:lgPp615xLHxN84RBd+viA/oHzJfI0miFYFH4T9wpPQ4=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
k8s.io/code-generator v0.0.0-20190311093542-50b561225d70/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8=
|
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||||
k8s.io/gengo v0.0.0-20181106084056-51747d6e00da h1:ZMvcXtMVbhUCtCuiSEzBV+Eur4swzfdxx6ZyX3qT6dk=
|
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||||
k8s.io/gengo v0.0.0-20181106084056-51747d6e00da/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 h1:8r+l4bNWjRlsFYlQJnKJ2p7s1YQPj4XyXiJVqDHRx7c=
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
k8s.io/api v0.21.1 h1:94bbZ5NTjdINJEdzOkpS4vdPhkb1VFpTYC9zh43f75c=
|
||||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s=
|
||||||
|
k8s.io/apiextensions-apiserver v0.21.1 h1:AA+cnsb6w7SZ1vD32Z+zdgfXdXY8X9uGX5bN6EoPEIo=
|
||||||
|
k8s.io/apiextensions-apiserver v0.21.1/go.mod h1:KESQFCGjqVcVsZ9g0xX5bacMjyX5emuWcS2arzdEouA=
|
||||||
|
k8s.io/apimachinery v0.21.1 h1:Q6XuHGlj2xc+hlMCvqyYfbv3H7SRGn2c8NycxJquDVs=
|
||||||
|
k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY=
|
||||||
|
k8s.io/apiserver v0.21.1/go.mod h1:nLLYZvMWn35glJ4/FZRhzLG/3MPxAaZTgV4FJZdr+tY=
|
||||||
|
k8s.io/client-go v0.21.1 h1:bhblWYLZKUu+pm50plvQF8WpY6TXdRRtcS/K9WauOj4=
|
||||||
|
k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs=
|
||||||
|
k8s.io/code-generator v0.21.1 h1:jvcxHpVu5dm/LMXr3GOj/jroiP8+v2YnJE9i2OVRenk=
|
||||||
|
k8s.io/code-generator v0.21.1/go.mod h1:hUlps5+9QaTrKx+jiM4rmq7YmH8wPOIko64uZCHDh6Q=
|
||||||
|
k8s.io/component-base v0.21.1/go.mod h1:NgzFZ2qu4m1juby4TnrmpR8adRk6ka62YdH5DkIIyKA=
|
||||||
|
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||||
|
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027 h1:Uusb3oh8XcdzDF/ndlI4ToKTYVlkCSJP39SRY2mfRAw=
|
||||||
|
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||||
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
|
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||||
|
k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts=
|
||||||
|
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
|
||||||
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
||||||
|
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.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
|
||||||
|
sigs.k8s.io/controller-tools v0.6.0 h1:o2Fm1K7CmIp8OVaBtXsWB/ssBAzyoKZPPAGR3VuxaKs=
|
||||||
|
sigs.k8s.io/controller-tools v0.6.0/go.mod h1:baRMVPrctU77F+rfAuH2uPqW93k6yQnZA2dhUOr7ihc=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
92
manifests/crds.yaml
Normal file
92
manifests/crds.yaml
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.6.0
|
||||||
|
creationTimestamp: null
|
||||||
|
name: peers.kilo.squat.ai
|
||||||
|
spec:
|
||||||
|
group: kilo.squat.ai
|
||||||
|
names:
|
||||||
|
kind: Peer
|
||||||
|
listKind: PeerList
|
||||||
|
plural: peers
|
||||||
|
singular: peer
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Peer is a WireGuard peer that should have access to the VPN.
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
of an object. Servers should convert recognized schemas to the latest
|
||||||
|
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: 'Kind is a string value representing the REST resource this
|
||||||
|
object represents. Servers may infer this from the endpoint the client
|
||||||
|
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
spec:
|
||||||
|
description: 'Specification of the desired behavior of the Kilo Peer.
|
||||||
|
More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status'
|
||||||
|
properties:
|
||||||
|
allowedIPs:
|
||||||
|
description: AllowedIPs is the list of IP addresses that are allowed
|
||||||
|
for the given peer's tunnel.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
endpoint:
|
||||||
|
description: Endpoint is the initial endpoint for connections to the
|
||||||
|
peer.
|
||||||
|
properties:
|
||||||
|
dnsOrIP:
|
||||||
|
description: DNSOrIP is a DNS name or an IP address.
|
||||||
|
properties:
|
||||||
|
dns:
|
||||||
|
description: DNS must be a valid RFC 1123 subdomain.
|
||||||
|
type: string
|
||||||
|
ip:
|
||||||
|
description: IP must be a valid IP address.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
port:
|
||||||
|
description: Port must be a valid port number.
|
||||||
|
format: int32
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- dnsOrIP
|
||||||
|
- port
|
||||||
|
type: object
|
||||||
|
persistentKeepalive:
|
||||||
|
description: PersistentKeepalive is the interval in seconds of the
|
||||||
|
emission of keepalive packets by the peer. This defaults to 0, which
|
||||||
|
disables the feature.
|
||||||
|
type: integer
|
||||||
|
presharedKey:
|
||||||
|
description: PresharedKey is the optional symmetric encryption key
|
||||||
|
for the peer.
|
||||||
|
type: string
|
||||||
|
publicKey:
|
||||||
|
description: PublicKey is the WireGuard public key for the peer.
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- allowedIPs
|
||||||
|
- publicKey
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- spec
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
@@ -30,7 +30,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -52,14 +52,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: kilo
|
serviceAccountName: kilo
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -77,6 +80,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
@@ -64,7 +64,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -86,14 +86,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: kilo
|
serviceAccountName: kilo
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -108,6 +111,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
@@ -30,7 +30,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -52,14 +52,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: kilo
|
serviceAccountName: kilo
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -77,6 +80,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
@@ -65,7 +65,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -87,14 +87,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
nkml.squat.ai/wireguard: "true"
|
nkml.squat.ai/wireguard: "true"
|
||||||
@@ -112,6 +115,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
@@ -189,14 +195,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo-userspace
|
app.kubernetes.io/name: kilo-userspace
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo-userspace
|
app.kubernetes.io/name: kilo-userspace
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo-userspace
|
app.kubernetes.io/name: kilo-userspace
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
nkml.squat.ai/wireguard: "false"
|
nkml.squat.ai/wireguard: "false"
|
||||||
@@ -215,6 +224,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
@@ -64,7 +64,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -85,15 +85,18 @@ metadata:
|
|||||||
name: kilo
|
name: kilo
|
||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo-userspace
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo-userspace
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo-userspace
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: kilo
|
serviceAccountName: kilo
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -110,6 +113,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
@@ -64,7 +64,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -86,14 +86,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: kilo
|
serviceAccountName: kilo
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -108,6 +111,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
@@ -30,7 +30,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -52,14 +52,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: kilo
|
serviceAccountName: kilo
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -77,6 +80,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
@@ -64,7 +64,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -86,14 +86,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: kilo
|
serviceAccountName: kilo
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -108,6 +111,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
@@ -30,7 +30,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -52,14 +52,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: kilo
|
serviceAccountName: kilo
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -77,6 +80,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
@@ -64,7 +64,7 @@ rules:
|
|||||||
resources:
|
resources:
|
||||||
- customresourcedefinitions
|
- customresourcedefinitions
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- get
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
@@ -86,14 +86,17 @@ metadata:
|
|||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: kilo
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: kilo
|
serviceAccountName: kilo
|
||||||
hostNetwork: true
|
hostNetwork: true
|
||||||
@@ -108,6 +111,9 @@ spec:
|
|||||||
valueFrom:
|
valueFrom:
|
||||||
fieldRef:
|
fieldRef:
|
||||||
fieldPath: spec.nodeName
|
fieldPath: spec.nodeName
|
||||||
|
ports:
|
||||||
|
- containerPort: 1107
|
||||||
|
name: metrics
|
||||||
securityContext:
|
securityContext:
|
||||||
privileged: true
|
privileged: true
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
19
manifests/podmonitor.yaml
Normal file
19
manifests/podmonitor.yaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: monitoring.coreos.com/v1
|
||||||
|
kind: PodMonitor
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: kilo
|
||||||
|
app.kubernetes.io/part-of: kilo
|
||||||
|
name: kilo
|
||||||
|
namespace: kilo
|
||||||
|
spec:
|
||||||
|
namespaceSelector:
|
||||||
|
matchNames:
|
||||||
|
- kube-system
|
||||||
|
podMetricsEndpoints:
|
||||||
|
- interval: 15s
|
||||||
|
port: metrics
|
||||||
|
path: /metrics
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/part-of: kilo
|
@@ -16,5 +16,5 @@ package kilo
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// GroupName contains the API group name for Kilo API group.
|
// GroupName contains the API group name for Kilo API group.
|
||||||
GroupName = "kilo"
|
GroupName = "kilo.squat.ai"
|
||||||
)
|
)
|
||||||
|
@@ -15,5 +15,5 @@
|
|||||||
// +k8s:deepcopy-gen=package,register
|
// +k8s:deepcopy-gen=package,register
|
||||||
|
|
||||||
// Package v1alpha1 is the v1alpha1 version of the API.
|
// Package v1alpha1 is the v1alpha1 version of the API.
|
||||||
// +groupName=kilo
|
// +groupName=kilo.squat.ai
|
||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@
|
|||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -76,20 +77,21 @@ type PeerSpec struct {
|
|||||||
PersistentKeepalive int `json:"persistentKeepalive,omitempty"`
|
PersistentKeepalive int `json:"persistentKeepalive,omitempty"`
|
||||||
// PresharedKey is the optional symmetric encryption key for the peer.
|
// PresharedKey is the optional symmetric encryption key for the peer.
|
||||||
// +optional
|
// +optional
|
||||||
PresharedKey string `json:"presharedKey"`
|
PresharedKey string `json:"presharedKey,omitempty"`
|
||||||
// PublicKey is the WireGuard public key for the peer.
|
// PublicKey is the WireGuard public key for the peer.
|
||||||
PublicKey string `json:"publicKey"`
|
PublicKey string `json:"publicKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerEndpoint represents a WireGuard enpoint, which is a ip:port tuple.
|
// PeerEndpoint represents a WireGuard endpoint, which is an IP:port tuple.
|
||||||
type PeerEndpoint struct {
|
type PeerEndpoint struct {
|
||||||
DNSOrIP
|
// DNSOrIP is a DNS name or an IP address.
|
||||||
|
DNSOrIP `json:"dnsOrIP"`
|
||||||
// Port must be a valid port number.
|
// Port must be a valid port number.
|
||||||
Port uint32 `json:"port"`
|
Port uint32 `json:"port"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSOrIP represents either a DNS name or an IP address.
|
// DNSOrIP represents either a DNS name or an IP address.
|
||||||
// IPs, as they are more specific, are preferred.
|
// When both are given, the IP address, as it is more specific, override the DNS name.
|
||||||
type DNSOrIP struct {
|
type DNSOrIP struct {
|
||||||
// DNS must be a valid RFC 1123 subdomain.
|
// DNS must be a valid RFC 1123 subdomain.
|
||||||
// +optional
|
// +optional
|
||||||
@@ -133,7 +135,7 @@ func (p *Peer) Copy() *Peer {
|
|||||||
func (p *Peer) Validate() error {
|
func (p *Peer) Validate() error {
|
||||||
for _, ip := range p.Spec.AllowedIPs {
|
for _, ip := range p.Spec.AllowedIPs {
|
||||||
if _, n, err := net.ParseCIDR(ip); err != nil {
|
if _, n, err := net.ParseCIDR(ip); err != nil {
|
||||||
return fmt.Errorf("failed to parse %q as a valid IP address: %v", ip, err)
|
return fmt.Errorf("failed to parse %q as a valid IP address: %w", ip, err)
|
||||||
} else if n == nil {
|
} else if n == nil {
|
||||||
return fmt.Errorf("got invalid IP address for %q", ip)
|
return fmt.Errorf("got invalid IP address for %q", ip)
|
||||||
}
|
}
|
||||||
@@ -157,8 +159,11 @@ func (p *Peer) Validate() error {
|
|||||||
if p.Spec.PersistentKeepalive < 0 {
|
if p.Spec.PersistentKeepalive < 0 {
|
||||||
return fmt.Errorf("persistent keepalive must be greater than or equal to zero; got %q", p.Spec.PersistentKeepalive)
|
return fmt.Errorf("persistent keepalive must be greater than or equal to zero; got %q", p.Spec.PersistentKeepalive)
|
||||||
}
|
}
|
||||||
if len(p.Spec.PublicKey) == 0 {
|
if b, err := base64.StdEncoding.DecodeString(p.Spec.PublicKey); err != nil {
|
||||||
return errors.New("public keys cannot be empty")
|
return fmt.Errorf("WireGuard public key is not base64 encoded: %w", err)
|
||||||
|
// Since WireGuard is using Curve25519 for the key exchange, the key length of 256 bits should not change in the near future.
|
||||||
|
} else if len(b) != 32 {
|
||||||
|
return errors.New("WireGuard public key has invalid length")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -168,6 +173,9 @@ func (p *Peer) Validate() error {
|
|||||||
// PeerList is a list of peers.
|
// PeerList is a list of peers.
|
||||||
type PeerList struct {
|
type PeerList struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
// Standard list metadata.
|
||||||
|
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
// +optional
|
||||||
metav1.ListMeta `json:"metadata,omitempty"`
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
// List of peers.
|
// List of peers.
|
||||||
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md
|
// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md
|
||||||
|
@@ -86,7 +86,7 @@ func (in *PeerEndpoint) DeepCopy() *PeerEndpoint {
|
|||||||
func (in *PeerList) DeepCopyInto(out *PeerList) {
|
func (in *PeerList) DeepCopyInto(out *PeerList) {
|
||||||
*out = *in
|
*out = *in
|
||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
out.ListMeta = in.ListMeta
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
if in.Items != nil {
|
if in.Items != nil {
|
||||||
in, out := &in.Items, &out.Items
|
in, out := &in.Items, &out.Items
|
||||||
*out = make([]Peer, len(*in))
|
*out = make([]Peer, len(*in))
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
package k8s
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -24,11 +25,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
crdutils "github.com/ant31/crd-validation/pkg"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
|
||||||
apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
@@ -59,6 +58,9 @@ const (
|
|||||||
locationAnnotationKey = "kilo.squat.ai/location"
|
locationAnnotationKey = "kilo.squat.ai/location"
|
||||||
persistentKeepaliveKey = "kilo.squat.ai/persistent-keepalive"
|
persistentKeepaliveKey = "kilo.squat.ai/persistent-keepalive"
|
||||||
wireGuardIPAnnotationKey = "kilo.squat.ai/wireguard-ip"
|
wireGuardIPAnnotationKey = "kilo.squat.ai/wireguard-ip"
|
||||||
|
discoveredEndpointsKey = "kilo.squat.ai/discovered-endpoints"
|
||||||
|
allowedLocationIPsKey = "kilo.squat.ai/allowed-location-ips"
|
||||||
|
granularityKey = "kilo.squat.ai/granularity"
|
||||||
// RegionLabelKey is the key for the well-known Kubernetes topology region label.
|
// RegionLabelKey is the key for the well-known Kubernetes topology region label.
|
||||||
RegionLabelKey = "topology.kubernetes.io/region"
|
RegionLabelKey = "topology.kubernetes.io/region"
|
||||||
jsonPatchSlash = "~1"
|
jsonPatchSlash = "~1"
|
||||||
@@ -127,8 +129,10 @@ func (nb *nodeBackend) CleanUp(name string) error {
|
|||||||
fmt.Sprintf(jsonRemovePatch, path.Join("/metadata", "annotations", strings.Replace(keyAnnotationKey, "/", jsonPatchSlash, 1))),
|
fmt.Sprintf(jsonRemovePatch, path.Join("/metadata", "annotations", strings.Replace(keyAnnotationKey, "/", jsonPatchSlash, 1))),
|
||||||
fmt.Sprintf(jsonRemovePatch, path.Join("/metadata", "annotations", strings.Replace(lastSeenAnnotationKey, "/", jsonPatchSlash, 1))),
|
fmt.Sprintf(jsonRemovePatch, path.Join("/metadata", "annotations", strings.Replace(lastSeenAnnotationKey, "/", jsonPatchSlash, 1))),
|
||||||
fmt.Sprintf(jsonRemovePatch, path.Join("/metadata", "annotations", strings.Replace(wireGuardIPAnnotationKey, "/", jsonPatchSlash, 1))),
|
fmt.Sprintf(jsonRemovePatch, path.Join("/metadata", "annotations", strings.Replace(wireGuardIPAnnotationKey, "/", jsonPatchSlash, 1))),
|
||||||
|
fmt.Sprintf(jsonRemovePatch, path.Join("/metadata", "annotations", strings.Replace(discoveredEndpointsKey, "/", jsonPatchSlash, 1))),
|
||||||
|
fmt.Sprintf(jsonRemovePatch, path.Join("/metadata", "annotations", strings.Replace(granularityKey, "/", jsonPatchSlash, 1))),
|
||||||
}, ",") + "]")
|
}, ",") + "]")
|
||||||
if _, err := nb.client.CoreV1().Nodes().Patch(name, types.JSONPatchType, patch); err != nil {
|
if _, err := nb.client.CoreV1().Nodes().Patch(context.TODO(), name, types.JSONPatchType, patch, metav1.PatchOptions{}); err != nil {
|
||||||
return fmt.Errorf("failed to patch node: %v", err)
|
return fmt.Errorf("failed to patch node: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -221,6 +225,16 @@ func (nb *nodeBackend) Set(name string, node *mesh.Node) error {
|
|||||||
} else {
|
} else {
|
||||||
n.ObjectMeta.Annotations[wireGuardIPAnnotationKey] = node.WireGuardIP.String()
|
n.ObjectMeta.Annotations[wireGuardIPAnnotationKey] = node.WireGuardIP.String()
|
||||||
}
|
}
|
||||||
|
if node.DiscoveredEndpoints == nil {
|
||||||
|
n.ObjectMeta.Annotations[discoveredEndpointsKey] = ""
|
||||||
|
} else {
|
||||||
|
discoveredEndpoints, err := json.Marshal(node.DiscoveredEndpoints)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n.ObjectMeta.Annotations[discoveredEndpointsKey] = string(discoveredEndpoints)
|
||||||
|
}
|
||||||
|
n.ObjectMeta.Annotations[granularityKey] = string(node.Granularity)
|
||||||
oldData, err := json.Marshal(old)
|
oldData, err := json.Marshal(old)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -233,7 +247,7 @@ func (nb *nodeBackend) Set(name string, node *mesh.Node) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create patch for node %q: %v", n.Name, err)
|
return fmt.Errorf("failed to create patch for node %q: %v", n.Name, err)
|
||||||
}
|
}
|
||||||
if _, err = nb.client.CoreV1().Nodes().Patch(name, types.StrategicMergePatchType, patch); err != nil {
|
if _, err = nb.client.CoreV1().Nodes().Patch(context.TODO(), name, types.StrategicMergePatchType, patch, metav1.PatchOptions{}); err != nil {
|
||||||
return fmt.Errorf("failed to patch node: %v", err)
|
return fmt.Errorf("failed to patch node: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -294,6 +308,33 @@ func translateNode(node *v1.Node, topologyLabel string) *mesh.Node {
|
|||||||
lastSeen = 0
|
lastSeen = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var discoveredEndpoints map[string]*wireguard.Endpoint
|
||||||
|
if de, ok := node.ObjectMeta.Annotations[discoveredEndpointsKey]; ok {
|
||||||
|
err := json.Unmarshal([]byte(de), &discoveredEndpoints)
|
||||||
|
if err != nil {
|
||||||
|
discoveredEndpoints = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set allowed IPs for a location.
|
||||||
|
var allowedLocationIPs []*net.IPNet
|
||||||
|
if str, ok := node.ObjectMeta.Annotations[allowedLocationIPsKey]; ok {
|
||||||
|
for _, ip := range strings.Split(str, ",") {
|
||||||
|
if ipnet := normalizeIP(ip); ipnet != nil {
|
||||||
|
allowedLocationIPs = append(allowedLocationIPs, ipnet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var meshGranularity mesh.Granularity
|
||||||
|
if gr, ok := node.ObjectMeta.Annotations[granularityKey]; ok {
|
||||||
|
meshGranularity = mesh.Granularity(gr)
|
||||||
|
switch meshGranularity {
|
||||||
|
case mesh.LogicalGranularity:
|
||||||
|
case mesh.FullGranularity:
|
||||||
|
default:
|
||||||
|
meshGranularity = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &mesh.Node{
|
return &mesh.Node{
|
||||||
// Endpoint and InternalIP should only ever fail to parse if the
|
// Endpoint and InternalIP should only ever fail to parse if the
|
||||||
// remote node's agent has not yet set its IP address;
|
// remote node's agent has not yet set its IP address;
|
||||||
@@ -315,6 +356,9 @@ func translateNode(node *v1.Node, topologyLabel string) *mesh.Node {
|
|||||||
// the node's agent has not yet reconciled. In either case, the IP
|
// the node's agent has not yet reconciled. In either case, the IP
|
||||||
// will parse as nil.
|
// will parse as nil.
|
||||||
WireGuardIP: normalizeIP(node.ObjectMeta.Annotations[wireGuardIPAnnotationKey]),
|
WireGuardIP: normalizeIP(node.ObjectMeta.Annotations[wireGuardIPAnnotationKey]),
|
||||||
|
DiscoveredEndpoints: discoveredEndpoints,
|
||||||
|
AllowedLocationIPs: allowedLocationIPs,
|
||||||
|
Granularity: meshGranularity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,24 +435,9 @@ func (pb *peerBackend) Get(name string) (*mesh.Peer, error) {
|
|||||||
// Init initializes the backend; for this backend that means
|
// Init initializes the backend; for this backend that means
|
||||||
// syncing the informer cache.
|
// syncing the informer cache.
|
||||||
func (pb *peerBackend) Init(stop <-chan struct{}) error {
|
func (pb *peerBackend) Init(stop <-chan struct{}) error {
|
||||||
// Register CRD.
|
// Check the presents of the CRD peers.kilo.squat.ai.
|
||||||
crd := crdutils.NewCustomResourceDefinition(crdutils.Config{
|
if _, err := pb.extensionsClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.TODO(), strings.Join([]string{v1alpha1.PeerPlural, v1alpha1.GroupName}, "."), metav1.GetOptions{}); err != nil {
|
||||||
SpecDefinitionName: "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1.Peer",
|
return fmt.Errorf("CRD is not present: %v", err)
|
||||||
EnableValidation: true,
|
|
||||||
ResourceScope: string(v1beta1.ClusterScoped),
|
|
||||||
Group: v1alpha1.GroupName,
|
|
||||||
Kind: v1alpha1.PeerKind,
|
|
||||||
Version: v1alpha1.SchemeGroupVersion.Version,
|
|
||||||
Plural: v1alpha1.PeerPlural,
|
|
||||||
ShortNames: v1alpha1.PeerShortNames,
|
|
||||||
GetOpenAPIDefinitions: v1alpha1.GetOpenAPIDefinitions,
|
|
||||||
})
|
|
||||||
crd.Spec.Subresources.Scale = nil
|
|
||||||
crd.Spec.Subresources.Status = nil
|
|
||||||
|
|
||||||
_, err := pb.extensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
|
||||||
if err != nil && !apierrors.IsAlreadyExists(err) {
|
|
||||||
return fmt.Errorf("failed to create CRD: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go pb.informer.Run(stop)
|
go pb.informer.Run(stop)
|
||||||
@@ -497,7 +526,7 @@ func (pb *peerBackend) Set(name string, peer *mesh.Peer) error {
|
|||||||
p.Spec.PersistentKeepalive = peer.PersistentKeepalive
|
p.Spec.PersistentKeepalive = peer.PersistentKeepalive
|
||||||
p.Spec.PresharedKey = string(peer.PresharedKey)
|
p.Spec.PresharedKey = string(peer.PresharedKey)
|
||||||
p.Spec.PublicKey = string(peer.PublicKey)
|
p.Spec.PublicKey = string(peer.PublicKey)
|
||||||
if _, err = pb.client.KiloV1alpha1().Peers().Update(p); err != nil {
|
if _, err = pb.client.KiloV1alpha1().Peers().Update(context.TODO(), p, metav1.UpdateOptions{}); err != nil {
|
||||||
return fmt.Errorf("failed to update peer: %v", err)
|
return fmt.Errorf("failed to update peer: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@@ -17,6 +17,8 @@
|
|||||||
package versioned
|
package versioned
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
kilov1alpha1 "github.com/squat/kilo/pkg/k8s/clientset/versioned/typed/kilo/v1alpha1"
|
kilov1alpha1 "github.com/squat/kilo/pkg/k8s/clientset/versioned/typed/kilo/v1alpha1"
|
||||||
discovery "k8s.io/client-go/discovery"
|
discovery "k8s.io/client-go/discovery"
|
||||||
rest "k8s.io/client-go/rest"
|
rest "k8s.io/client-go/rest"
|
||||||
@@ -49,9 +51,14 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewForConfig creates a new Clientset for the given config.
|
// NewForConfig creates a new Clientset for the given config.
|
||||||
|
// If config's RateLimiter is not set and QPS and Burst are acceptable,
|
||||||
|
// NewForConfig will generate a rate-limiter in configShallowCopy.
|
||||||
func NewForConfig(c *rest.Config) (*Clientset, error) {
|
func NewForConfig(c *rest.Config) (*Clientset, error) {
|
||||||
configShallowCopy := *c
|
configShallowCopy := *c
|
||||||
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
|
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
|
||||||
|
if configShallowCopy.Burst <= 0 {
|
||||||
|
return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
|
||||||
|
}
|
||||||
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
|
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
|
||||||
}
|
}
|
||||||
var cs Clientset
|
var cs Clientset
|
||||||
|
@@ -39,7 +39,7 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := &Clientset{}
|
cs := &Clientset{tracker: o}
|
||||||
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
|
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
|
||||||
cs.AddReactor("*", "*", testing.ObjectReaction(o))
|
cs.AddReactor("*", "*", testing.ObjectReaction(o))
|
||||||
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
|
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
|
||||||
@@ -61,12 +61,17 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset {
|
|||||||
type Clientset struct {
|
type Clientset struct {
|
||||||
testing.Fake
|
testing.Fake
|
||||||
discovery *fakediscovery.FakeDiscovery
|
discovery *fakediscovery.FakeDiscovery
|
||||||
|
tracker testing.ObjectTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
|
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
|
||||||
return c.discovery
|
return c.discovery
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Clientset) Tracker() testing.ObjectTracker {
|
||||||
|
return c.tracker
|
||||||
|
}
|
||||||
|
|
||||||
var _ clientset.Interface = &Clientset{}
|
var _ clientset.Interface = &Clientset{}
|
||||||
|
|
||||||
// KiloV1alpha1 retrieves the KiloV1alpha1Client
|
// KiloV1alpha1 retrieves the KiloV1alpha1Client
|
||||||
|
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
var scheme = runtime.NewScheme()
|
var scheme = runtime.NewScheme()
|
||||||
var codecs = serializer.NewCodecFactory(scheme)
|
var codecs = serializer.NewCodecFactory(scheme)
|
||||||
var parameterCodec = runtime.NewParameterCodec(scheme)
|
|
||||||
var localSchemeBuilder = runtime.SchemeBuilder{
|
var localSchemeBuilder = runtime.SchemeBuilder{
|
||||||
kilov1alpha1.AddToScheme,
|
kilov1alpha1.AddToScheme,
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,8 @@
|
|||||||
package fake
|
package fake
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
v1alpha1 "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
v1alpha1 "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
labels "k8s.io/apimachinery/pkg/labels"
|
labels "k8s.io/apimachinery/pkg/labels"
|
||||||
@@ -31,12 +33,12 @@ type FakePeers struct {
|
|||||||
Fake *FakeKiloV1alpha1
|
Fake *FakeKiloV1alpha1
|
||||||
}
|
}
|
||||||
|
|
||||||
var peersResource = schema.GroupVersionResource{Group: "kilo", Version: "v1alpha1", Resource: "peers"}
|
var peersResource = schema.GroupVersionResource{Group: "kilo.squat.ai", Version: "v1alpha1", Resource: "peers"}
|
||||||
|
|
||||||
var peersKind = schema.GroupVersionKind{Group: "kilo", Version: "v1alpha1", Kind: "Peer"}
|
var peersKind = schema.GroupVersionKind{Group: "kilo.squat.ai", Version: "v1alpha1", Kind: "Peer"}
|
||||||
|
|
||||||
// Get takes name of the peer, and returns the corresponding peer object, and an error if there is any.
|
// Get takes name of the peer, and returns the corresponding peer object, and an error if there is any.
|
||||||
func (c *FakePeers) Get(name string, options v1.GetOptions) (result *v1alpha1.Peer, err error) {
|
func (c *FakePeers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Peer, err error) {
|
||||||
obj, err := c.Fake.
|
obj, err := c.Fake.
|
||||||
Invokes(testing.NewRootGetAction(peersResource, name), &v1alpha1.Peer{})
|
Invokes(testing.NewRootGetAction(peersResource, name), &v1alpha1.Peer{})
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
@@ -46,7 +48,7 @@ func (c *FakePeers) Get(name string, options v1.GetOptions) (result *v1alpha1.Pe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List takes label and field selectors, and returns the list of Peers that match those selectors.
|
// List takes label and field selectors, and returns the list of Peers that match those selectors.
|
||||||
func (c *FakePeers) List(opts v1.ListOptions) (result *v1alpha1.PeerList, err error) {
|
func (c *FakePeers) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PeerList, err error) {
|
||||||
obj, err := c.Fake.
|
obj, err := c.Fake.
|
||||||
Invokes(testing.NewRootListAction(peersResource, peersKind, opts), &v1alpha1.PeerList{})
|
Invokes(testing.NewRootListAction(peersResource, peersKind, opts), &v1alpha1.PeerList{})
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
@@ -67,13 +69,13 @@ func (c *FakePeers) List(opts v1.ListOptions) (result *v1alpha1.PeerList, err er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Watch returns a watch.Interface that watches the requested peers.
|
// Watch returns a watch.Interface that watches the requested peers.
|
||||||
func (c *FakePeers) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
func (c *FakePeers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||||
return c.Fake.
|
return c.Fake.
|
||||||
InvokesWatch(testing.NewRootWatchAction(peersResource, opts))
|
InvokesWatch(testing.NewRootWatchAction(peersResource, opts))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create takes the representation of a peer and creates it. Returns the server's representation of the peer, and an error, if there is any.
|
// Create takes the representation of a peer and creates it. Returns the server's representation of the peer, and an error, if there is any.
|
||||||
func (c *FakePeers) Create(peer *v1alpha1.Peer) (result *v1alpha1.Peer, err error) {
|
func (c *FakePeers) Create(ctx context.Context, peer *v1alpha1.Peer, opts v1.CreateOptions) (result *v1alpha1.Peer, err error) {
|
||||||
obj, err := c.Fake.
|
obj, err := c.Fake.
|
||||||
Invokes(testing.NewRootCreateAction(peersResource, peer), &v1alpha1.Peer{})
|
Invokes(testing.NewRootCreateAction(peersResource, peer), &v1alpha1.Peer{})
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
@@ -83,7 +85,7 @@ func (c *FakePeers) Create(peer *v1alpha1.Peer) (result *v1alpha1.Peer, err erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update takes the representation of a peer and updates it. Returns the server's representation of the peer, and an error, if there is any.
|
// Update takes the representation of a peer and updates it. Returns the server's representation of the peer, and an error, if there is any.
|
||||||
func (c *FakePeers) Update(peer *v1alpha1.Peer) (result *v1alpha1.Peer, err error) {
|
func (c *FakePeers) Update(ctx context.Context, peer *v1alpha1.Peer, opts v1.UpdateOptions) (result *v1alpha1.Peer, err error) {
|
||||||
obj, err := c.Fake.
|
obj, err := c.Fake.
|
||||||
Invokes(testing.NewRootUpdateAction(peersResource, peer), &v1alpha1.Peer{})
|
Invokes(testing.NewRootUpdateAction(peersResource, peer), &v1alpha1.Peer{})
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
@@ -93,22 +95,22 @@ func (c *FakePeers) Update(peer *v1alpha1.Peer) (result *v1alpha1.Peer, err erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete takes name of the peer and deletes it. Returns an error if one occurs.
|
// Delete takes name of the peer and deletes it. Returns an error if one occurs.
|
||||||
func (c *FakePeers) Delete(name string, options *v1.DeleteOptions) error {
|
func (c *FakePeers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||||
_, err := c.Fake.
|
_, err := c.Fake.
|
||||||
Invokes(testing.NewRootDeleteAction(peersResource, name), &v1alpha1.Peer{})
|
Invokes(testing.NewRootDeleteAction(peersResource, name), &v1alpha1.Peer{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCollection deletes a collection of objects.
|
// DeleteCollection deletes a collection of objects.
|
||||||
func (c *FakePeers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
func (c *FakePeers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||||
action := testing.NewRootDeleteCollectionAction(peersResource, listOptions)
|
action := testing.NewRootDeleteCollectionAction(peersResource, listOpts)
|
||||||
|
|
||||||
_, err := c.Fake.Invokes(action, &v1alpha1.PeerList{})
|
_, err := c.Fake.Invokes(action, &v1alpha1.PeerList{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch applies the patch and returns the patched peer.
|
// Patch applies the patch and returns the patched peer.
|
||||||
func (c *FakePeers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Peer, err error) {
|
func (c *FakePeers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Peer, err error) {
|
||||||
obj, err := c.Fake.
|
obj, err := c.Fake.
|
||||||
Invokes(testing.NewRootPatchSubresourceAction(peersResource, name, pt, data, subresources...), &v1alpha1.Peer{})
|
Invokes(testing.NewRootPatchSubresourceAction(peersResource, name, pt, data, subresources...), &v1alpha1.Peer{})
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
|
@@ -19,7 +19,6 @@ package v1alpha1
|
|||||||
import (
|
import (
|
||||||
v1alpha1 "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
v1alpha1 "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
||||||
"github.com/squat/kilo/pkg/k8s/clientset/versioned/scheme"
|
"github.com/squat/kilo/pkg/k8s/clientset/versioned/scheme"
|
||||||
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
|
||||||
rest "k8s.io/client-go/rest"
|
rest "k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,7 +27,7 @@ type KiloV1alpha1Interface interface {
|
|||||||
PeersGetter
|
PeersGetter
|
||||||
}
|
}
|
||||||
|
|
||||||
// KiloV1alpha1Client is used to interact with features provided by the kilo group.
|
// KiloV1alpha1Client is used to interact with features provided by the kilo.squat.ai group.
|
||||||
type KiloV1alpha1Client struct {
|
type KiloV1alpha1Client struct {
|
||||||
restClient rest.Interface
|
restClient rest.Interface
|
||||||
}
|
}
|
||||||
@@ -69,7 +68,7 @@ func setConfigDefaults(config *rest.Config) error {
|
|||||||
gv := v1alpha1.SchemeGroupVersion
|
gv := v1alpha1.SchemeGroupVersion
|
||||||
config.GroupVersion = &gv
|
config.GroupVersion = &gv
|
||||||
config.APIPath = "/apis"
|
config.APIPath = "/apis"
|
||||||
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
|
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
|
||||||
|
|
||||||
if config.UserAgent == "" {
|
if config.UserAgent == "" {
|
||||||
config.UserAgent = rest.DefaultKubernetesUserAgent()
|
config.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1alpha1 "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
v1alpha1 "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
||||||
@@ -35,14 +36,14 @@ type PeersGetter interface {
|
|||||||
|
|
||||||
// PeerInterface has methods to work with Peer resources.
|
// PeerInterface has methods to work with Peer resources.
|
||||||
type PeerInterface interface {
|
type PeerInterface interface {
|
||||||
Create(*v1alpha1.Peer) (*v1alpha1.Peer, error)
|
Create(ctx context.Context, peer *v1alpha1.Peer, opts v1.CreateOptions) (*v1alpha1.Peer, error)
|
||||||
Update(*v1alpha1.Peer) (*v1alpha1.Peer, error)
|
Update(ctx context.Context, peer *v1alpha1.Peer, opts v1.UpdateOptions) (*v1alpha1.Peer, error)
|
||||||
Delete(name string, options *v1.DeleteOptions) error
|
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
|
||||||
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
|
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
|
||||||
Get(name string, options v1.GetOptions) (*v1alpha1.Peer, error)
|
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Peer, error)
|
||||||
List(opts v1.ListOptions) (*v1alpha1.PeerList, error)
|
List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.PeerList, error)
|
||||||
Watch(opts v1.ListOptions) (watch.Interface, error)
|
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
|
||||||
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Peer, err error)
|
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Peer, err error)
|
||||||
PeerExpansion
|
PeerExpansion
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,19 +60,19 @@ func newPeers(c *KiloV1alpha1Client) *peers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get takes name of the peer, and returns the corresponding peer object, and an error if there is any.
|
// Get takes name of the peer, and returns the corresponding peer object, and an error if there is any.
|
||||||
func (c *peers) Get(name string, options v1.GetOptions) (result *v1alpha1.Peer, err error) {
|
func (c *peers) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Peer, err error) {
|
||||||
result = &v1alpha1.Peer{}
|
result = &v1alpha1.Peer{}
|
||||||
err = c.client.Get().
|
err = c.client.Get().
|
||||||
Resource("peers").
|
Resource("peers").
|
||||||
Name(name).
|
Name(name).
|
||||||
VersionedParams(&options, scheme.ParameterCodec).
|
VersionedParams(&options, scheme.ParameterCodec).
|
||||||
Do().
|
Do(ctx).
|
||||||
Into(result)
|
Into(result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// List takes label and field selectors, and returns the list of Peers that match those selectors.
|
// List takes label and field selectors, and returns the list of Peers that match those selectors.
|
||||||
func (c *peers) List(opts v1.ListOptions) (result *v1alpha1.PeerList, err error) {
|
func (c *peers) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.PeerList, err error) {
|
||||||
var timeout time.Duration
|
var timeout time.Duration
|
||||||
if opts.TimeoutSeconds != nil {
|
if opts.TimeoutSeconds != nil {
|
||||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||||
@@ -81,13 +82,13 @@ func (c *peers) List(opts v1.ListOptions) (result *v1alpha1.PeerList, err error)
|
|||||||
Resource("peers").
|
Resource("peers").
|
||||||
VersionedParams(&opts, scheme.ParameterCodec).
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
Timeout(timeout).
|
Timeout(timeout).
|
||||||
Do().
|
Do(ctx).
|
||||||
Into(result)
|
Into(result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch returns a watch.Interface that watches the requested peers.
|
// Watch returns a watch.Interface that watches the requested peers.
|
||||||
func (c *peers) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
func (c *peers) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) {
|
||||||
var timeout time.Duration
|
var timeout time.Duration
|
||||||
if opts.TimeoutSeconds != nil {
|
if opts.TimeoutSeconds != nil {
|
||||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||||
@@ -97,66 +98,69 @@ func (c *peers) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
|||||||
Resource("peers").
|
Resource("peers").
|
||||||
VersionedParams(&opts, scheme.ParameterCodec).
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
Timeout(timeout).
|
Timeout(timeout).
|
||||||
Watch()
|
Watch(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create takes the representation of a peer and creates it. Returns the server's representation of the peer, and an error, if there is any.
|
// Create takes the representation of a peer and creates it. Returns the server's representation of the peer, and an error, if there is any.
|
||||||
func (c *peers) Create(peer *v1alpha1.Peer) (result *v1alpha1.Peer, err error) {
|
func (c *peers) Create(ctx context.Context, peer *v1alpha1.Peer, opts v1.CreateOptions) (result *v1alpha1.Peer, err error) {
|
||||||
result = &v1alpha1.Peer{}
|
result = &v1alpha1.Peer{}
|
||||||
err = c.client.Post().
|
err = c.client.Post().
|
||||||
Resource("peers").
|
Resource("peers").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
Body(peer).
|
Body(peer).
|
||||||
Do().
|
Do(ctx).
|
||||||
Into(result)
|
Into(result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update takes the representation of a peer and updates it. Returns the server's representation of the peer, and an error, if there is any.
|
// Update takes the representation of a peer and updates it. Returns the server's representation of the peer, and an error, if there is any.
|
||||||
func (c *peers) Update(peer *v1alpha1.Peer) (result *v1alpha1.Peer, err error) {
|
func (c *peers) Update(ctx context.Context, peer *v1alpha1.Peer, opts v1.UpdateOptions) (result *v1alpha1.Peer, err error) {
|
||||||
result = &v1alpha1.Peer{}
|
result = &v1alpha1.Peer{}
|
||||||
err = c.client.Put().
|
err = c.client.Put().
|
||||||
Resource("peers").
|
Resource("peers").
|
||||||
Name(peer.Name).
|
Name(peer.Name).
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
Body(peer).
|
Body(peer).
|
||||||
Do().
|
Do(ctx).
|
||||||
Into(result)
|
Into(result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete takes name of the peer and deletes it. Returns an error if one occurs.
|
// Delete takes name of the peer and deletes it. Returns an error if one occurs.
|
||||||
func (c *peers) Delete(name string, options *v1.DeleteOptions) error {
|
func (c *peers) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
|
||||||
return c.client.Delete().
|
return c.client.Delete().
|
||||||
Resource("peers").
|
Resource("peers").
|
||||||
Name(name).
|
Name(name).
|
||||||
Body(options).
|
Body(&opts).
|
||||||
Do().
|
Do(ctx).
|
||||||
Error()
|
Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCollection deletes a collection of objects.
|
// DeleteCollection deletes a collection of objects.
|
||||||
func (c *peers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
func (c *peers) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error {
|
||||||
var timeout time.Duration
|
var timeout time.Duration
|
||||||
if listOptions.TimeoutSeconds != nil {
|
if listOpts.TimeoutSeconds != nil {
|
||||||
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
|
timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second
|
||||||
}
|
}
|
||||||
return c.client.Delete().
|
return c.client.Delete().
|
||||||
Resource("peers").
|
Resource("peers").
|
||||||
VersionedParams(&listOptions, scheme.ParameterCodec).
|
VersionedParams(&listOpts, scheme.ParameterCodec).
|
||||||
Timeout(timeout).
|
Timeout(timeout).
|
||||||
Body(options).
|
Body(&opts).
|
||||||
Do().
|
Do(ctx).
|
||||||
Error()
|
Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch applies the patch and returns the patched peer.
|
// Patch applies the patch and returns the patched peer.
|
||||||
func (c *peers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Peer, err error) {
|
func (c *peers) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Peer, err error) {
|
||||||
result = &v1alpha1.Peer{}
|
result = &v1alpha1.Peer{}
|
||||||
err = c.client.Patch(pt).
|
err = c.client.Patch(pt).
|
||||||
Resource("peers").
|
Resource("peers").
|
||||||
SubResource(subresources...).
|
|
||||||
Name(name).
|
Name(name).
|
||||||
|
SubResource(subresources...).
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
Body(data).
|
Body(data).
|
||||||
Do().
|
Do(ctx).
|
||||||
Into(result)
|
Into(result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -50,7 +50,7 @@ func (f *genericInformer) Lister() cache.GenericLister {
|
|||||||
// TODO extend this to unknown resources with a client pool
|
// TODO extend this to unknown resources with a client pool
|
||||||
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
|
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
|
||||||
switch resource {
|
switch resource {
|
||||||
// Group=kilo, Version=v1alpha1
|
// Group=kilo.squat.ai, Version=v1alpha1
|
||||||
case v1alpha1.SchemeGroupVersion.WithResource("peers"):
|
case v1alpha1.SchemeGroupVersion.WithResource("peers"):
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Kilo().V1alpha1().Peers().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Kilo().V1alpha1().Peers().Informer()}, nil
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package v1alpha1
|
package v1alpha1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
time "time"
|
time "time"
|
||||||
|
|
||||||
kilov1alpha1 "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
kilov1alpha1 "github.com/squat/kilo/pkg/k8s/apis/kilo/v1alpha1"
|
||||||
@@ -58,13 +59,13 @@ func NewFilteredPeerInformer(client versioned.Interface, resyncPeriod time.Durat
|
|||||||
if tweakListOptions != nil {
|
if tweakListOptions != nil {
|
||||||
tweakListOptions(&options)
|
tweakListOptions(&options)
|
||||||
}
|
}
|
||||||
return client.KiloV1alpha1().Peers().List(options)
|
return client.KiloV1alpha1().Peers().List(context.TODO(), options)
|
||||||
},
|
},
|
||||||
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||||
if tweakListOptions != nil {
|
if tweakListOptions != nil {
|
||||||
tweakListOptions(&options)
|
tweakListOptions(&options)
|
||||||
}
|
}
|
||||||
return client.KiloV1alpha1().Peers().Watch(options)
|
return client.KiloV1alpha1().Peers().Watch(context.TODO(), options)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&kilov1alpha1.Peer{},
|
&kilov1alpha1.Peer{},
|
||||||
|
@@ -24,10 +24,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// PeerLister helps list Peers.
|
// PeerLister helps list Peers.
|
||||||
|
// All objects returned here must be treated as read-only.
|
||||||
type PeerLister interface {
|
type PeerLister interface {
|
||||||
// List lists all Peers in the indexer.
|
// List lists all Peers in the indexer.
|
||||||
|
// Objects returned here must be treated as read-only.
|
||||||
List(selector labels.Selector) (ret []*v1alpha1.Peer, err error)
|
List(selector labels.Selector) (ret []*v1alpha1.Peer, err error)
|
||||||
// Get retrieves the Peer from the index for a given name.
|
// Get retrieves the Peer from the index for a given name.
|
||||||
|
// Objects returned here must be treated as read-only.
|
||||||
Get(name string) (*v1alpha1.Peer, error)
|
Get(name string) (*v1alpha1.Peer, error)
|
||||||
PeerListerExpansion
|
PeerListerExpansion
|
||||||
}
|
}
|
||||||
|
@@ -47,6 +47,9 @@ const (
|
|||||||
// FullGranularity indicates that the network should create
|
// FullGranularity indicates that the network should create
|
||||||
// a mesh between every node.
|
// a mesh between every node.
|
||||||
FullGranularity Granularity = "full"
|
FullGranularity Granularity = "full"
|
||||||
|
// AutoGranularity can be used with kgctl to obtain
|
||||||
|
// the granularity automatically.
|
||||||
|
AutoGranularity Granularity = "auto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Node represents a node in the network.
|
// Node represents a node in the network.
|
||||||
@@ -66,6 +69,9 @@ type Node struct {
|
|||||||
PersistentKeepalive int
|
PersistentKeepalive int
|
||||||
Subnet *net.IPNet
|
Subnet *net.IPNet
|
||||||
WireGuardIP *net.IPNet
|
WireGuardIP *net.IPNet
|
||||||
|
DiscoveredEndpoints map[string]*wireguard.Endpoint
|
||||||
|
AllowedLocationIPs []*net.IPNet
|
||||||
|
Granularity Granularity
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ready indicates whether or not the node is ready.
|
// Ready indicates whether or not the node is ready.
|
||||||
|
125
pkg/mesh/mesh.go
125
pkg/mesh/mesh.go
@@ -274,14 +274,10 @@ func (m *Mesh) syncNodes(e *NodeEvent) {
|
|||||||
var diff bool
|
var diff bool
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
if !e.Node.Ready() {
|
if !e.Node.Ready() {
|
||||||
level.Debug(logger).Log("msg", "received incomplete node", "node", e.Node)
|
// Trace non ready nodes with their presence in the mesh.
|
||||||
// An existing node is no longer valid
|
_, ok := m.nodes[e.Node.Name]
|
||||||
// so remove it from the mesh.
|
level.Debug(logger).Log("msg", "received non ready node", "node", e.Node, "in-mesh", ok)
|
||||||
if _, ok := m.nodes[e.Node.Name]; ok {
|
|
||||||
level.Info(logger).Log("msg", "node is no longer ready", "node", e.Node)
|
|
||||||
diff = true
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
case AddEvent:
|
case AddEvent:
|
||||||
fallthrough
|
fallthrough
|
||||||
@@ -296,7 +292,6 @@ func (m *Mesh) syncNodes(e *NodeEvent) {
|
|||||||
delete(m.nodes, e.Node.Name)
|
delete(m.nodes, e.Node.Name)
|
||||||
diff = true
|
diff = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
if diff {
|
if diff {
|
||||||
level.Info(logger).Log("node", e.Node)
|
level.Info(logger).Log("node", e.Node)
|
||||||
@@ -312,14 +307,10 @@ func (m *Mesh) syncPeers(e *PeerEvent) {
|
|||||||
// Peers are indexed by public key.
|
// Peers are indexed by public key.
|
||||||
key := string(e.Peer.PublicKey)
|
key := string(e.Peer.PublicKey)
|
||||||
if !e.Peer.Ready() {
|
if !e.Peer.Ready() {
|
||||||
level.Debug(logger).Log("msg", "received incomplete peer", "peer", e.Peer)
|
// Trace non ready peer with their presence in the mesh.
|
||||||
// An existing peer is no longer valid
|
_, ok := m.peers[key]
|
||||||
// so remove it from the mesh.
|
level.Debug(logger).Log("msg", "received non ready peer", "peer", e.Peer, "in-mesh", ok)
|
||||||
if _, ok := m.peers[key]; ok {
|
|
||||||
level.Info(logger).Log("msg", "peer is no longer ready", "peer", e.Peer)
|
|
||||||
diff = true
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch e.Type {
|
switch e.Type {
|
||||||
case AddEvent:
|
case AddEvent:
|
||||||
fallthrough
|
fallthrough
|
||||||
@@ -336,7 +327,6 @@ func (m *Mesh) syncPeers(e *PeerEvent) {
|
|||||||
delete(m.peers, key)
|
delete(m.peers, key)
|
||||||
diff = true
|
diff = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
if diff {
|
if diff {
|
||||||
level.Info(logger).Log("peer", e.Peer)
|
level.Info(logger).Log("peer", e.Peer)
|
||||||
@@ -389,6 +379,9 @@ func (m *Mesh) handleLocal(n *Node) {
|
|||||||
PersistentKeepalive: n.PersistentKeepalive,
|
PersistentKeepalive: n.PersistentKeepalive,
|
||||||
Subnet: n.Subnet,
|
Subnet: n.Subnet,
|
||||||
WireGuardIP: m.wireGuardIP,
|
WireGuardIP: m.wireGuardIP,
|
||||||
|
DiscoveredEndpoints: n.DiscoveredEndpoints,
|
||||||
|
AllowedLocationIPs: n.AllowedLocationIPs,
|
||||||
|
Granularity: m.granularity,
|
||||||
}
|
}
|
||||||
if !nodesAreEqual(n, local) {
|
if !nodesAreEqual(n, local) {
|
||||||
level.Debug(m.logger).Log("msg", "local node differs from backend")
|
level.Debug(m.logger).Log("msg", "local node differs from backend")
|
||||||
@@ -428,12 +421,12 @@ func (m *Mesh) applyTopology() {
|
|||||||
nodes := make(map[string]*Node)
|
nodes := make(map[string]*Node)
|
||||||
var readyNodes float64
|
var readyNodes float64
|
||||||
for k := range m.nodes {
|
for k := range m.nodes {
|
||||||
|
m.nodes[k].Granularity = m.granularity
|
||||||
if !m.nodes[k].Ready() {
|
if !m.nodes[k].Ready() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Make a shallow copy of the node.
|
// Make it point to the node without copy.
|
||||||
node := *m.nodes[k]
|
nodes[k] = m.nodes[k]
|
||||||
nodes[k] = &node
|
|
||||||
readyNodes++
|
readyNodes++
|
||||||
}
|
}
|
||||||
// Ensure only ready nodes are considered.
|
// Ensure only ready nodes are considered.
|
||||||
@@ -443,9 +436,8 @@ func (m *Mesh) applyTopology() {
|
|||||||
if !m.peers[k].Ready() {
|
if !m.peers[k].Ready() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Make a shallow copy of the peer.
|
// Make it point the peer without copy.
|
||||||
peer := *m.peers[k]
|
peers[k] = m.peers[k]
|
||||||
peers[k] = &peer
|
|
||||||
readyPeers++
|
readyPeers++
|
||||||
}
|
}
|
||||||
m.nodesGuage.Set(readyNodes)
|
m.nodesGuage.Set(readyNodes)
|
||||||
@@ -462,15 +454,21 @@ func (m *Mesh) applyTopology() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Find the old configuration.
|
// Find the old configuration.
|
||||||
oldConfRaw, err := wireguard.ShowConf(link.Attrs().Name)
|
oldConfDump, err := wireguard.ShowDump(link.Attrs().Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
level.Error(m.logger).Log("error", err)
|
level.Error(m.logger).Log("error", err)
|
||||||
m.errorCounter.WithLabelValues("apply").Inc()
|
m.errorCounter.WithLabelValues("apply").Inc()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
oldConf := wireguard.Parse(oldConfRaw)
|
oldConf, err := wireguard.ParseDump(oldConfDump)
|
||||||
updateNATEndpoints(nodes, peers, oldConf)
|
if err != nil {
|
||||||
t, err := NewTopology(nodes, peers, m.granularity, m.hostname, nodes[m.hostname].Endpoint.Port, m.priv, m.subnet, nodes[m.hostname].PersistentKeepalive)
|
level.Error(m.logger).Log("error", err)
|
||||||
|
m.errorCounter.WithLabelValues("apply").Inc()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
natEndpoints := discoverNATEndpoints(nodes, peers, oldConf, m.logger)
|
||||||
|
nodes[m.hostname].DiscoveredEndpoints = natEndpoints
|
||||||
|
t, err := NewTopology(nodes, peers, m.granularity, m.hostname, nodes[m.hostname].Endpoint.Port, m.priv, m.subnet, nodes[m.hostname].PersistentKeepalive, m.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
level.Error(m.logger).Log("error", err)
|
level.Error(m.logger).Log("error", err)
|
||||||
m.errorCounter.WithLabelValues("apply").Inc()
|
m.errorCounter.WithLabelValues("apply").Inc()
|
||||||
@@ -676,26 +674,15 @@ func nodesAreEqual(a, b *Node) bool {
|
|||||||
if a == b {
|
if a == b {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if !(a.Endpoint != nil) == (b.Endpoint != nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if a.Endpoint != nil {
|
|
||||||
if a.Endpoint.Port != b.Endpoint.Port {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Check the DNS name first since this package
|
// Check the DNS name first since this package
|
||||||
// is doing the DNS resolution.
|
// is doing the DNS resolution.
|
||||||
if a.Endpoint.DNS != b.Endpoint.DNS {
|
if !a.Endpoint.Equal(b.Endpoint, true) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if a.Endpoint.DNS == "" && !a.Endpoint.IP.Equal(b.Endpoint.IP) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ignore LastSeen when comparing equality we want to check if the nodes are
|
// Ignore LastSeen when comparing equality we want to check if the nodes are
|
||||||
// equivalent. However, we do want to check if LastSeen has transitioned
|
// equivalent. However, we do want to check if LastSeen has transitioned
|
||||||
// between valid and invalid.
|
// between valid and invalid.
|
||||||
return string(a.Key) == string(b.Key) && ipNetsEqual(a.WireGuardIP, b.WireGuardIP) && ipNetsEqual(a.InternalIP, b.InternalIP) && a.Leader == b.Leader && a.Location == b.Location && a.Name == b.Name && subnetsEqual(a.Subnet, b.Subnet) && a.Ready() == b.Ready() && a.PersistentKeepalive == b.PersistentKeepalive
|
return string(a.Key) == string(b.Key) && ipNetsEqual(a.WireGuardIP, b.WireGuardIP) && ipNetsEqual(a.InternalIP, b.InternalIP) && a.Leader == b.Leader && a.Location == b.Location && a.Name == b.Name && subnetsEqual(a.Subnet, b.Subnet) && a.Ready() == b.Ready() && a.PersistentKeepalive == b.PersistentKeepalive && discoveredEndpointsAreEqual(a.DiscoveredEndpoints, b.DiscoveredEndpoints) && ipNetSlicesEqual(a.AllowedLocationIPs, b.AllowedLocationIPs) && a.Granularity == b.Granularity
|
||||||
}
|
}
|
||||||
|
|
||||||
func peersAreEqual(a, b *Peer) bool {
|
func peersAreEqual(a, b *Peer) bool {
|
||||||
@@ -705,22 +692,11 @@ func peersAreEqual(a, b *Peer) bool {
|
|||||||
if a == b {
|
if a == b {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if !(a.Endpoint != nil) == (b.Endpoint != nil) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if a.Endpoint != nil {
|
|
||||||
if a.Endpoint.Port != b.Endpoint.Port {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Check the DNS name first since this package
|
// Check the DNS name first since this package
|
||||||
// is doing the DNS resolution.
|
// is doing the DNS resolution.
|
||||||
if a.Endpoint.DNS != b.Endpoint.DNS {
|
if !a.Endpoint.Equal(b.Endpoint, true) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if a.Endpoint.DNS == "" && !a.Endpoint.IP.Equal(b.Endpoint.IP) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(a.AllowedIPs) != len(b.AllowedIPs) {
|
if len(a.AllowedIPs) != len(b.AllowedIPs) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -745,6 +721,18 @@ func ipNetsEqual(a, b *net.IPNet) bool {
|
|||||||
return a.IP.Equal(b.IP)
|
return a.IP.Equal(b.IP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ipNetSlicesEqual(a, b []*net.IPNet) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range a {
|
||||||
|
if !ipNetsEqual(a[i], b[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func subnetsEqual(a, b *net.IPNet) bool {
|
func subnetsEqual(a, b *net.IPNet) bool {
|
||||||
if a == nil && b == nil {
|
if a == nil && b == nil {
|
||||||
return true
|
return true
|
||||||
@@ -764,6 +752,24 @@ func subnetsEqual(a, b *net.IPNet) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func discoveredEndpointsAreEqual(a, b map[string]*wireguard.Endpoint) bool {
|
||||||
|
if a == nil && b == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (a != nil) != (b != nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for k := range a {
|
||||||
|
if !a[k].Equal(b[k], false) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func linkByIndex(index int) (netlink.Link, error) {
|
func linkByIndex(index int) (netlink.Link, error) {
|
||||||
link, err := netlink.LinkByIndex(index)
|
link, err := netlink.LinkByIndex(index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -772,21 +778,28 @@ func linkByIndex(index int) (netlink.Link, error) {
|
|||||||
return link, nil
|
return link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateNATEndpoints ensures that nodes and peers behind NAT update
|
// discoverNATEndpoints uses the node's WireGuard configuration to returns a list of the most recently discovered endpoints for all nodes and peers behind NAT so that they can roam.
|
||||||
// their endpoints from the WireGuard configuration so they can roam.
|
func discoverNATEndpoints(nodes map[string]*Node, peers map[string]*Peer, conf *wireguard.Conf, logger log.Logger) map[string]*wireguard.Endpoint {
|
||||||
func updateNATEndpoints(nodes map[string]*Node, peers map[string]*Peer, conf *wireguard.Conf) {
|
natEndpoints := make(map[string]*wireguard.Endpoint)
|
||||||
keys := make(map[string]*wireguard.Peer)
|
keys := make(map[string]*wireguard.Peer)
|
||||||
for i := range conf.Peers {
|
for i := range conf.Peers {
|
||||||
keys[string(conf.Peers[i].PublicKey)] = conf.Peers[i]
|
keys[string(conf.Peers[i].PublicKey)] = conf.Peers[i]
|
||||||
}
|
}
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
if peer, ok := keys[string(n.Key)]; ok && n.PersistentKeepalive > 0 {
|
if peer, ok := keys[string(n.Key)]; ok && n.PersistentKeepalive > 0 {
|
||||||
n.Endpoint = peer.Endpoint
|
level.Debug(logger).Log("msg", "WireGuard Update NAT Endpoint", "node", n.Name, "endpoint", peer.Endpoint, "former-endpoint", n.Endpoint, "same", n.Endpoint.Equal(peer.Endpoint, false), "latest-handshake", peer.LatestHandshake)
|
||||||
|
if (peer.LatestHandshake != time.Time{}) {
|
||||||
|
natEndpoints[string(n.Key)] = peer.Endpoint
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, p := range peers {
|
for _, p := range peers {
|
||||||
if peer, ok := keys[string(p.PublicKey)]; ok && p.PersistentKeepalive > 0 {
|
if peer, ok := keys[string(p.PublicKey)]; ok && p.PersistentKeepalive > 0 {
|
||||||
p.Endpoint = peer.Endpoint
|
if (peer.LatestHandshake != time.Time{}) {
|
||||||
|
natEndpoints[string(p.PublicKey)] = peer.Endpoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
level.Debug(logger).Log("msg", "Discovered WireGuard NAT Endpoints", "DiscoveredEndpoints", natEndpoints)
|
||||||
|
return natEndpoints
|
||||||
|
}
|
||||||
|
@@ -108,6 +108,17 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
}, enc.Strategy(), t.privateIP, tunlIface))
|
}, enc.Strategy(), t.privateIP, tunlIface))
|
||||||
}
|
}
|
||||||
|
// For segments / locations other than the location of this instance of kg,
|
||||||
|
// we need to set routes for allowed location IPs over the leader in the current location.
|
||||||
|
for i := range segment.allowedLocationIPs {
|
||||||
|
routes = append(routes, encapsulateRoute(&netlink.Route{
|
||||||
|
Dst: segment.allowedLocationIPs[i],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: gw,
|
||||||
|
LinkIndex: privIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
}, enc.Strategy(), t.privateIP, tunlIface))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Add routes for the allowed IPs of peers.
|
// Add routes for the allowed IPs of peers.
|
||||||
for _, peer := range t.peers {
|
for _, peer := range t.peers {
|
||||||
@@ -198,6 +209,17 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
|
|||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// For segments / locations other than the location of this instance of kg,
|
||||||
|
// we need to set routes for allowed location IPs over the wg interface.
|
||||||
|
for i := range segment.allowedLocationIPs {
|
||||||
|
routes = append(routes, &netlink.Route{
|
||||||
|
Dst: segment.allowedLocationIPs[i],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: segment.wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Add routes for the allowed IPs of peers.
|
// Add routes for the allowed IPs of peers.
|
||||||
for _, peer := range t.peers {
|
for _, peer := range t.peers {
|
||||||
@@ -232,6 +254,16 @@ func (t *Topology) Rules(cni bool) []iptables.Rule {
|
|||||||
for _, aip := range s.allowedIPs {
|
for _, aip := range s.allowedIPs {
|
||||||
rules = append(rules, iptables.NewRule(iptables.GetProtocol(len(aip.IP)), "nat", "KILO-NAT", "-d", aip.String(), "-m", "comment", "--comment", "Kilo: do not NAT packets destined for known IPs", "-j", "RETURN"))
|
rules = append(rules, iptables.NewRule(iptables.GetProtocol(len(aip.IP)), "nat", "KILO-NAT", "-d", aip.String(), "-m", "comment", "--comment", "Kilo: do not NAT packets destined for known IPs", "-j", "RETURN"))
|
||||||
}
|
}
|
||||||
|
// Make sure packets to allowed location IPs go through the KILO-NAT chain, so they can be MASQUERADEd,
|
||||||
|
// Otherwise packets to these destinations will reach the destination, but never find their way back.
|
||||||
|
// We only want to NAT in locations of the corresponding allowed location IPs.
|
||||||
|
if t.location == s.location {
|
||||||
|
for _, alip := range s.allowedLocationIPs {
|
||||||
|
rules = append(rules,
|
||||||
|
iptables.NewRule(iptables.GetProtocol(len(alip.IP)), "nat", "POSTROUTING", "-d", alip.String(), "-m", "comment", "--comment", "Kilo: jump to NAT chain", "-j", "KILO-NAT"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, p := range t.peers {
|
for _, p := range t.peers {
|
||||||
for _, aip := range p.AllowedIPs {
|
for _, aip := range p.AllowedIPs {
|
||||||
|
@@ -74,6 +74,13 @@ func TestRoutes(t *testing.T) {
|
|||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Dst: nodes["b"].AllowedLocationIPs[0],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Dst: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[2].cidrs[0],
|
Dst: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[2].cidrs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
@@ -258,6 +265,13 @@ func TestRoutes(t *testing.T) {
|
|||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Dst: nodes["b"].AllowedLocationIPs[0],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["d"].Name).segments[1].wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Dst: peers["a"].AllowedIPs[0],
|
Dst: peers["a"].AllowedIPs[0],
|
||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
@@ -294,6 +308,13 @@ func TestRoutes(t *testing.T) {
|
|||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Dst: nodes["b"].AllowedLocationIPs[0],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Dst: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name).segments[2].cidrs[0],
|
Dst: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name).segments[2].cidrs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
@@ -422,6 +443,13 @@ func TestRoutes(t *testing.T) {
|
|||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Dst: nodes["b"].AllowedLocationIPs[0],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name).segments[1].wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Dst: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name).segments[3].cidrs[0],
|
Dst: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name).segments[3].cidrs[0],
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
@@ -480,6 +508,13 @@ func TestRoutes(t *testing.T) {
|
|||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Dst: nodes["b"].AllowedLocationIPs[0],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["d"].Subnet,
|
Dst: nodes["d"].Subnet,
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
@@ -538,6 +573,13 @@ func TestRoutes(t *testing.T) {
|
|||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Dst: nodes["b"].AllowedLocationIPs[0],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["d"].Subnet,
|
Dst: nodes["d"].Subnet,
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
@@ -875,6 +917,13 @@ func TestRoutes(t *testing.T) {
|
|||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Dst: nodes["b"].AllowedLocationIPs[0],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["c"].Subnet,
|
Dst: nodes["c"].Subnet,
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
@@ -1005,6 +1054,13 @@ func TestRoutes(t *testing.T) {
|
|||||||
LinkIndex: kiloIface,
|
LinkIndex: kiloIface,
|
||||||
Protocol: unix.RTPROT_STATIC,
|
Protocol: unix.RTPROT_STATIC,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Dst: nodes["b"].AllowedLocationIPs[0],
|
||||||
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
Gw: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name).segments[1].wireGuardIP,
|
||||||
|
LinkIndex: kiloIface,
|
||||||
|
Protocol: unix.RTPROT_STATIC,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Dst: nodes["d"].Subnet,
|
Dst: nodes["d"].Subnet,
|
||||||
Flags: int(netlink.FLAG_ONLINK),
|
Flags: int(netlink.FLAG_ONLINK),
|
||||||
|
@@ -19,6 +19,9 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/go-kit/kit/log"
|
||||||
|
"github.com/go-kit/kit/log/level"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/wireguard"
|
"github.com/squat/kilo/pkg/wireguard"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,12 +58,16 @@ type Topology struct {
|
|||||||
// the IP is the 0th address in the subnet, i.e. the CIDR
|
// the IP is the 0th address in the subnet, i.e. the CIDR
|
||||||
// is equal to the Kilo subnet.
|
// is equal to the Kilo subnet.
|
||||||
wireGuardCIDR *net.IPNet
|
wireGuardCIDR *net.IPNet
|
||||||
|
// discoveredEndpoints is the updated map of valid discovered Endpoints
|
||||||
|
discoveredEndpoints map[string]*wireguard.Endpoint
|
||||||
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type segment struct {
|
type segment struct {
|
||||||
allowedIPs []*net.IPNet
|
allowedIPs []*net.IPNet
|
||||||
endpoint *wireguard.Endpoint
|
endpoint *wireguard.Endpoint
|
||||||
key []byte
|
key []byte
|
||||||
|
persistentKeepalive int
|
||||||
// Location is the logical location of this segment.
|
// Location is the logical location of this segment.
|
||||||
location string
|
location string
|
||||||
|
|
||||||
@@ -75,10 +82,17 @@ type segment struct {
|
|||||||
// wireGuardIP is the allocated IP address of the WireGuard
|
// wireGuardIP is the allocated IP address of the WireGuard
|
||||||
// interface on the leader of the segment.
|
// interface on the leader of the segment.
|
||||||
wireGuardIP net.IP
|
wireGuardIP net.IP
|
||||||
|
// allowedLocationIPs are not part of the cluster and are not peers.
|
||||||
|
// They are directly routable from nodes within the segment.
|
||||||
|
// A classic example is a printer that ought to be routable from other locations.
|
||||||
|
allowedLocationIPs []*net.IPNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTopology creates a new Topology struct from a given set of nodes and peers.
|
// NewTopology creates a new Topology struct from a given set of nodes and peers.
|
||||||
func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port uint32, key []byte, subnet *net.IPNet, persistentKeepalive int) (*Topology, error) {
|
func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port uint32, key []byte, subnet *net.IPNet, persistentKeepalive int, logger log.Logger) (*Topology, error) {
|
||||||
|
if logger == nil {
|
||||||
|
logger = log.NewNopLogger()
|
||||||
|
}
|
||||||
topoMap := make(map[string][]*Node)
|
topoMap := make(map[string][]*Node)
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
var location string
|
var location string
|
||||||
@@ -106,7 +120,7 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
localLocation = nodeLocationPrefix + hostname
|
localLocation = nodeLocationPrefix + hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
t := Topology{key: key, port: port, hostname: hostname, location: localLocation, persistentKeepalive: persistentKeepalive, privateIP: nodes[hostname].InternalIP, subnet: nodes[hostname].Subnet, wireGuardCIDR: subnet}
|
t := Topology{key: key, port: port, hostname: hostname, location: localLocation, persistentKeepalive: persistentKeepalive, privateIP: nodes[hostname].InternalIP, subnet: nodes[hostname].Subnet, wireGuardCIDR: subnet, discoveredEndpoints: make(map[string]*wireguard.Endpoint), logger: logger}
|
||||||
for location := range topoMap {
|
for location := range topoMap {
|
||||||
// Sort the location so the result is stable.
|
// Sort the location so the result is stable.
|
||||||
sort.Slice(topoMap[location], func(i, j int) bool {
|
sort.Slice(topoMap[location], func(i, j int) bool {
|
||||||
@@ -117,6 +131,8 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
t.leader = true
|
t.leader = true
|
||||||
}
|
}
|
||||||
var allowedIPs []*net.IPNet
|
var allowedIPs []*net.IPNet
|
||||||
|
allowedLocationIPsMap := make(map[string]struct{})
|
||||||
|
var allowedLocationIPs []*net.IPNet
|
||||||
var cidrs []*net.IPNet
|
var cidrs []*net.IPNet
|
||||||
var hostnames []string
|
var hostnames []string
|
||||||
var privateIPs []net.IP
|
var privateIPs []net.IP
|
||||||
@@ -125,7 +141,14 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
// - the node's allocated subnet
|
// - the node's allocated subnet
|
||||||
// - the node's WireGuard IP
|
// - the node's WireGuard IP
|
||||||
// - the node's internal IP
|
// - the node's internal IP
|
||||||
|
// - IPs that were specified by the allowed-location-ips annotation
|
||||||
allowedIPs = append(allowedIPs, node.Subnet)
|
allowedIPs = append(allowedIPs, node.Subnet)
|
||||||
|
for _, ip := range node.AllowedLocationIPs {
|
||||||
|
if _, ok := allowedLocationIPsMap[ip.String()]; !ok {
|
||||||
|
allowedLocationIPs = append(allowedLocationIPs, ip)
|
||||||
|
allowedLocationIPsMap[ip.String()] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
if node.InternalIP != nil {
|
if node.InternalIP != nil {
|
||||||
allowedIPs = append(allowedIPs, oneAddressCIDR(node.InternalIP.IP))
|
allowedIPs = append(allowedIPs, oneAddressCIDR(node.InternalIP.IP))
|
||||||
privateIPs = append(privateIPs, node.InternalIP.IP)
|
privateIPs = append(privateIPs, node.InternalIP.IP)
|
||||||
@@ -133,15 +156,21 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
cidrs = append(cidrs, node.Subnet)
|
cidrs = append(cidrs, node.Subnet)
|
||||||
hostnames = append(hostnames, node.Name)
|
hostnames = append(hostnames, node.Name)
|
||||||
}
|
}
|
||||||
|
// The sorting has no function, but makes testing easier.
|
||||||
|
sort.Slice(allowedLocationIPs, func(i, j int) bool {
|
||||||
|
return allowedLocationIPs[i].String() < allowedLocationIPs[j].String()
|
||||||
|
})
|
||||||
t.segments = append(t.segments, &segment{
|
t.segments = append(t.segments, &segment{
|
||||||
allowedIPs: allowedIPs,
|
allowedIPs: allowedIPs,
|
||||||
endpoint: topoMap[location][leader].Endpoint,
|
endpoint: topoMap[location][leader].Endpoint,
|
||||||
key: topoMap[location][leader].Key,
|
key: topoMap[location][leader].Key,
|
||||||
|
persistentKeepalive: topoMap[location][leader].PersistentKeepalive,
|
||||||
location: location,
|
location: location,
|
||||||
cidrs: cidrs,
|
cidrs: cidrs,
|
||||||
hostnames: hostnames,
|
hostnames: hostnames,
|
||||||
leader: leader,
|
leader: leader,
|
||||||
privateIPs: privateIPs,
|
privateIPs: privateIPs,
|
||||||
|
allowedLocationIPs: allowedLocationIPs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Sort the Topology segments so the result is stable.
|
// Sort the Topology segments so the result is stable.
|
||||||
@@ -159,6 +188,10 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
// We need to defensively deduplicate peer allowed IPs. If two peers claim the same IP,
|
// We need to defensively deduplicate peer allowed IPs. If two peers claim the same IP,
|
||||||
// the WireGuard configuration could flap, causing the interface to churn.
|
// the WireGuard configuration could flap, causing the interface to churn.
|
||||||
t.peers = deduplicatePeerIPs(t.peers)
|
t.peers = deduplicatePeerIPs(t.peers)
|
||||||
|
// Copy the host node DiscoveredEndpoints in the topology as a starting point.
|
||||||
|
for key := range nodes[hostname].DiscoveredEndpoints {
|
||||||
|
t.discoveredEndpoints[key] = nodes[hostname].DiscoveredEndpoints[key]
|
||||||
|
}
|
||||||
// Allocate IPs to the segment leaders in a stable, coordination-free manner.
|
// Allocate IPs to the segment leaders in a stable, coordination-free manner.
|
||||||
a := newAllocator(*subnet)
|
a := newAllocator(*subnet)
|
||||||
for _, segment := range t.segments {
|
for _, segment := range t.segments {
|
||||||
@@ -171,11 +204,81 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
|
|||||||
if t.leader && segment.location == t.location {
|
if t.leader && segment.location == t.location {
|
||||||
t.wireGuardCIDR = &net.IPNet{IP: ipNet.IP, Mask: subnet.Mask}
|
t.wireGuardCIDR = &net.IPNet{IP: ipNet.IP, Mask: subnet.Mask}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that the topology is ordered, update the discoveredEndpoints map
|
||||||
|
// add new ones by going through the ordered topology: segments, nodes
|
||||||
|
for _, node := range topoMap[segment.location] {
|
||||||
|
for key := range node.DiscoveredEndpoints {
|
||||||
|
if _, ok := t.discoveredEndpoints[key]; !ok {
|
||||||
|
t.discoveredEndpoints[key] = node.DiscoveredEndpoints[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for intersecting IPs in allowed location IPs
|
||||||
|
segment.allowedLocationIPs = t.filterAllowedLocationIPs(segment.allowedLocationIPs, segment.location)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &t, nil
|
return &t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func intersect(n1, n2 *net.IPNet) bool {
|
||||||
|
return n1.Contains(n2.IP) || n2.Contains(n1.IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Topology) filterAllowedLocationIPs(ips []*net.IPNet, location string) (ret []*net.IPNet) {
|
||||||
|
CheckIPs:
|
||||||
|
for _, ip := range ips {
|
||||||
|
for _, s := range t.segments {
|
||||||
|
// Check if allowed location IPs are also allowed in other locations.
|
||||||
|
if location != s.location {
|
||||||
|
for _, i := range s.allowedLocationIPs {
|
||||||
|
if intersect(ip, i) {
|
||||||
|
level.Warn(t.logger).Log("msg", "overlapping allowed location IPnets", "IP", ip.String(), "IP2", i.String(), "segment-location", s.location)
|
||||||
|
continue CheckIPs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if allowed location IPs intersect with the allowed IPs.
|
||||||
|
for _, i := range s.allowedIPs {
|
||||||
|
if intersect(ip, i) {
|
||||||
|
level.Warn(t.logger).Log("msg", "overlapping allowed location IPnet with allowed IPnets", "IP", ip.String(), "IP2", i.String(), "segment-location", s.location)
|
||||||
|
continue CheckIPs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if allowed location IPs intersect with the private IPs of the segment.
|
||||||
|
for _, i := range s.privateIPs {
|
||||||
|
if ip.Contains(i) {
|
||||||
|
level.Warn(t.logger).Log("msg", "overlapping allowed location IPnet with privateIP", "IP", ip.String(), "IP2", i.String(), "segment-location", s.location)
|
||||||
|
continue CheckIPs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if allowed location IPs intersect with allowed IPs of peers.
|
||||||
|
for _, p := range t.peers {
|
||||||
|
for _, i := range p.AllowedIPs {
|
||||||
|
if intersect(ip, i) {
|
||||||
|
level.Warn(t.logger).Log("msg", "overlapping allowed location IPnet with peer IPnet", "IP", ip.String(), "IP2", i.String(), "peer", p.Name)
|
||||||
|
continue CheckIPs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = append(ret, ip)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Topology) updateEndpoint(endpoint *wireguard.Endpoint, key []byte, persistentKeepalive int) *wireguard.Endpoint {
|
||||||
|
// Do not update non-nat peers
|
||||||
|
if persistentKeepalive == 0 {
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
e, ok := t.discoveredEndpoints[string(key)]
|
||||||
|
if ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
|
||||||
// Conf generates a WireGuard configuration file for a given Topology.
|
// Conf generates a WireGuard configuration file for a given Topology.
|
||||||
func (t *Topology) Conf() *wireguard.Conf {
|
func (t *Topology) Conf() *wireguard.Conf {
|
||||||
c := &wireguard.Conf{
|
c := &wireguard.Conf{
|
||||||
@@ -189,8 +292,8 @@ func (t *Topology) Conf() *wireguard.Conf {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
peer := &wireguard.Peer{
|
peer := &wireguard.Peer{
|
||||||
AllowedIPs: s.allowedIPs,
|
AllowedIPs: append(s.allowedIPs, s.allowedLocationIPs...),
|
||||||
Endpoint: s.endpoint,
|
Endpoint: t.updateEndpoint(s.endpoint, s.key, s.persistentKeepalive),
|
||||||
PersistentKeepalive: t.persistentKeepalive,
|
PersistentKeepalive: t.persistentKeepalive,
|
||||||
PublicKey: s.key,
|
PublicKey: s.key,
|
||||||
}
|
}
|
||||||
@@ -199,7 +302,7 @@ func (t *Topology) Conf() *wireguard.Conf {
|
|||||||
for _, p := range t.peers {
|
for _, p := range t.peers {
|
||||||
peer := &wireguard.Peer{
|
peer := &wireguard.Peer{
|
||||||
AllowedIPs: p.AllowedIPs,
|
AllowedIPs: p.AllowedIPs,
|
||||||
Endpoint: p.Endpoint,
|
Endpoint: t.updateEndpoint(p.Endpoint, p.PublicKey, p.PersistentKeepalive),
|
||||||
PersistentKeepalive: t.persistentKeepalive,
|
PersistentKeepalive: t.persistentKeepalive,
|
||||||
PresharedKey: p.PresharedKey,
|
PresharedKey: p.PresharedKey,
|
||||||
PublicKey: p.PublicKey,
|
PublicKey: p.PublicKey,
|
||||||
|
@@ -19,6 +19,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-kit/kit/log"
|
||||||
"github.com/kylelemons/godebug/pretty"
|
"github.com/kylelemons/godebug/pretty"
|
||||||
|
|
||||||
"github.com/squat/kilo/pkg/wireguard"
|
"github.com/squat/kilo/pkg/wireguard"
|
||||||
@@ -28,6 +29,15 @@ func allowedIPs(ips ...string) string {
|
|||||||
return strings.Join(ips, ", ")
|
return strings.Join(ips, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mustParseCIDR(s string) (r *net.IPNet) {
|
||||||
|
if _, ip, err := net.ParseCIDR(s); err != nil {
|
||||||
|
panic("failed to parse CIDR")
|
||||||
|
} else {
|
||||||
|
r = ip
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
|
func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
|
||||||
key := []byte("private")
|
key := []byte("private")
|
||||||
e1 := &net.IPNet{IP: net.ParseIP("10.1.0.1").To4(), Mask: net.CIDRMask(16, 32)}
|
e1 := &net.IPNet{IP: net.ParseIP("10.1.0.1").To4(), Mask: net.CIDRMask(16, 32)}
|
||||||
@@ -36,6 +46,7 @@ func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
|
|||||||
e4 := &net.IPNet{IP: net.ParseIP("10.1.0.4").To4(), Mask: net.CIDRMask(16, 32)}
|
e4 := &net.IPNet{IP: net.ParseIP("10.1.0.4").To4(), Mask: net.CIDRMask(16, 32)}
|
||||||
i1 := &net.IPNet{IP: net.ParseIP("192.168.0.1").To4(), Mask: net.CIDRMask(32, 32)}
|
i1 := &net.IPNet{IP: net.ParseIP("192.168.0.1").To4(), Mask: net.CIDRMask(32, 32)}
|
||||||
i2 := &net.IPNet{IP: net.ParseIP("192.168.0.2").To4(), Mask: net.CIDRMask(32, 32)}
|
i2 := &net.IPNet{IP: net.ParseIP("192.168.0.2").To4(), Mask: net.CIDRMask(32, 32)}
|
||||||
|
i3 := &net.IPNet{IP: net.ParseIP("192.168.178.3").To4(), Mask: net.CIDRMask(32, 32)}
|
||||||
nodes := map[string]*Node{
|
nodes := map[string]*Node{
|
||||||
"a": {
|
"a": {
|
||||||
Name: "a",
|
Name: "a",
|
||||||
@@ -53,6 +64,7 @@ func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
|
|||||||
Location: "2",
|
Location: "2",
|
||||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.2.0"), Mask: net.CIDRMask(24, 32)},
|
Subnet: &net.IPNet{IP: net.ParseIP("10.2.2.0"), Mask: net.CIDRMask(24, 32)},
|
||||||
Key: []byte("key2"),
|
Key: []byte("key2"),
|
||||||
|
AllowedLocationIPs: []*net.IPNet{i3},
|
||||||
},
|
},
|
||||||
"c": {
|
"c": {
|
||||||
Name: "c",
|
Name: "c",
|
||||||
@@ -129,6 +141,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
location: logicalLocationPrefix + nodes["a"].Location,
|
location: logicalLocationPrefix + nodes["a"].Location,
|
||||||
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
||||||
hostnames: []string{"a"},
|
hostnames: []string{"a"},
|
||||||
@@ -139,16 +152,19 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
location: logicalLocationPrefix + nodes["b"].Location,
|
location: logicalLocationPrefix + nodes["b"].Location,
|
||||||
cidrs: []*net.IPNet{nodes["b"].Subnet, nodes["c"].Subnet},
|
cidrs: []*net.IPNet{nodes["b"].Subnet, nodes["c"].Subnet},
|
||||||
hostnames: []string{"b", "c"},
|
hostnames: []string{"b", "c"},
|
||||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
|
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
|
||||||
wireGuardIP: w2,
|
wireGuardIP: w2,
|
||||||
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["d"].Name,
|
location: nodeLocationPrefix + nodes["d"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
||||||
hostnames: []string{"d"},
|
hostnames: []string{"d"},
|
||||||
@@ -157,6 +173,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
peers: []*Peer{peers["a"], peers["b"]},
|
peers: []*Peer{peers["a"], peers["b"]},
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -175,6 +192,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
location: logicalLocationPrefix + nodes["a"].Location,
|
location: logicalLocationPrefix + nodes["a"].Location,
|
||||||
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
||||||
hostnames: []string{"a"},
|
hostnames: []string{"a"},
|
||||||
@@ -185,16 +203,19 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
location: logicalLocationPrefix + nodes["b"].Location,
|
location: logicalLocationPrefix + nodes["b"].Location,
|
||||||
cidrs: []*net.IPNet{nodes["b"].Subnet, nodes["c"].Subnet},
|
cidrs: []*net.IPNet{nodes["b"].Subnet, nodes["c"].Subnet},
|
||||||
hostnames: []string{"b", "c"},
|
hostnames: []string{"b", "c"},
|
||||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
|
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
|
||||||
wireGuardIP: w2,
|
wireGuardIP: w2,
|
||||||
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["d"].Name,
|
location: nodeLocationPrefix + nodes["d"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
||||||
hostnames: []string{"d"},
|
hostnames: []string{"d"},
|
||||||
@@ -203,6 +224,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
peers: []*Peer{peers["a"], peers["b"]},
|
peers: []*Peer{peers["a"], peers["b"]},
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -221,6 +243,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
location: logicalLocationPrefix + nodes["a"].Location,
|
location: logicalLocationPrefix + nodes["a"].Location,
|
||||||
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
||||||
hostnames: []string{"a"},
|
hostnames: []string{"a"},
|
||||||
@@ -231,16 +254,19 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
location: logicalLocationPrefix + nodes["b"].Location,
|
location: logicalLocationPrefix + nodes["b"].Location,
|
||||||
cidrs: []*net.IPNet{nodes["b"].Subnet, nodes["c"].Subnet},
|
cidrs: []*net.IPNet{nodes["b"].Subnet, nodes["c"].Subnet},
|
||||||
hostnames: []string{"b", "c"},
|
hostnames: []string{"b", "c"},
|
||||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
|
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
|
||||||
wireGuardIP: w2,
|
wireGuardIP: w2,
|
||||||
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["d"].Name,
|
location: nodeLocationPrefix + nodes["d"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
||||||
hostnames: []string{"d"},
|
hostnames: []string{"d"},
|
||||||
@@ -249,6 +275,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
peers: []*Peer{peers["a"], peers["b"]},
|
peers: []*Peer{peers["a"], peers["b"]},
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -267,6 +294,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["a"].Name,
|
location: nodeLocationPrefix + nodes["a"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
||||||
hostnames: []string{"a"},
|
hostnames: []string{"a"},
|
||||||
@@ -277,16 +305,19 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["b"].Name,
|
location: nodeLocationPrefix + nodes["b"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["b"].Subnet},
|
cidrs: []*net.IPNet{nodes["b"].Subnet},
|
||||||
hostnames: []string{"b"},
|
hostnames: []string{"b"},
|
||||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
||||||
wireGuardIP: w2,
|
wireGuardIP: w2,
|
||||||
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["c"].Endpoint,
|
endpoint: nodes["c"].Endpoint,
|
||||||
key: nodes["c"].Key,
|
key: nodes["c"].Key,
|
||||||
|
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["c"].Name,
|
location: nodeLocationPrefix + nodes["c"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["c"].Subnet},
|
cidrs: []*net.IPNet{nodes["c"].Subnet},
|
||||||
hostnames: []string{"c"},
|
hostnames: []string{"c"},
|
||||||
@@ -297,6 +328,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["d"].Name,
|
location: nodeLocationPrefix + nodes["d"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
||||||
hostnames: []string{"d"},
|
hostnames: []string{"d"},
|
||||||
@@ -305,6 +337,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
peers: []*Peer{peers["a"], peers["b"]},
|
peers: []*Peer{peers["a"], peers["b"]},
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -323,6 +356,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["a"].Name,
|
location: nodeLocationPrefix + nodes["a"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
||||||
hostnames: []string{"a"},
|
hostnames: []string{"a"},
|
||||||
@@ -333,16 +367,19 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["b"].Name,
|
location: nodeLocationPrefix + nodes["b"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["b"].Subnet},
|
cidrs: []*net.IPNet{nodes["b"].Subnet},
|
||||||
hostnames: []string{"b"},
|
hostnames: []string{"b"},
|
||||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
||||||
wireGuardIP: w2,
|
wireGuardIP: w2,
|
||||||
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["c"].Endpoint,
|
endpoint: nodes["c"].Endpoint,
|
||||||
key: nodes["c"].Key,
|
key: nodes["c"].Key,
|
||||||
|
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["c"].Name,
|
location: nodeLocationPrefix + nodes["c"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["c"].Subnet},
|
cidrs: []*net.IPNet{nodes["c"].Subnet},
|
||||||
hostnames: []string{"c"},
|
hostnames: []string{"c"},
|
||||||
@@ -353,6 +390,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["d"].Name,
|
location: nodeLocationPrefix + nodes["d"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
||||||
hostnames: []string{"d"},
|
hostnames: []string{"d"},
|
||||||
@@ -361,6 +399,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
peers: []*Peer{peers["a"], peers["b"]},
|
peers: []*Peer{peers["a"], peers["b"]},
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -379,6 +418,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["a"].Name,
|
location: nodeLocationPrefix + nodes["a"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
||||||
hostnames: []string{"a"},
|
hostnames: []string{"a"},
|
||||||
@@ -389,16 +429,19 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["b"].Name,
|
location: nodeLocationPrefix + nodes["b"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["b"].Subnet},
|
cidrs: []*net.IPNet{nodes["b"].Subnet},
|
||||||
hostnames: []string{"b"},
|
hostnames: []string{"b"},
|
||||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
||||||
wireGuardIP: w2,
|
wireGuardIP: w2,
|
||||||
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["c"].Endpoint,
|
endpoint: nodes["c"].Endpoint,
|
||||||
key: nodes["c"].Key,
|
key: nodes["c"].Key,
|
||||||
|
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["c"].Name,
|
location: nodeLocationPrefix + nodes["c"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["c"].Subnet},
|
cidrs: []*net.IPNet{nodes["c"].Subnet},
|
||||||
hostnames: []string{"c"},
|
hostnames: []string{"c"},
|
||||||
@@ -409,6 +452,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["d"].Name,
|
location: nodeLocationPrefix + nodes["d"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
||||||
hostnames: []string{"d"},
|
hostnames: []string{"d"},
|
||||||
@@ -417,6 +461,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
peers: []*Peer{peers["a"], peers["b"]},
|
peers: []*Peer{peers["a"], peers["b"]},
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -435,6 +480,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["a"].Endpoint,
|
endpoint: nodes["a"].Endpoint,
|
||||||
key: nodes["a"].Key,
|
key: nodes["a"].Key,
|
||||||
|
persistentKeepalive: nodes["a"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["a"].Name,
|
location: nodeLocationPrefix + nodes["a"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
cidrs: []*net.IPNet{nodes["a"].Subnet},
|
||||||
hostnames: []string{"a"},
|
hostnames: []string{"a"},
|
||||||
@@ -445,16 +491,19 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["b"].Endpoint,
|
endpoint: nodes["b"].Endpoint,
|
||||||
key: nodes["b"].Key,
|
key: nodes["b"].Key,
|
||||||
|
persistentKeepalive: nodes["b"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["b"].Name,
|
location: nodeLocationPrefix + nodes["b"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["b"].Subnet},
|
cidrs: []*net.IPNet{nodes["b"].Subnet},
|
||||||
hostnames: []string{"b"},
|
hostnames: []string{"b"},
|
||||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
||||||
wireGuardIP: w2,
|
wireGuardIP: w2,
|
||||||
|
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["c"].Endpoint,
|
endpoint: nodes["c"].Endpoint,
|
||||||
key: nodes["c"].Key,
|
key: nodes["c"].Key,
|
||||||
|
persistentKeepalive: nodes["c"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["c"].Name,
|
location: nodeLocationPrefix + nodes["c"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["c"].Subnet},
|
cidrs: []*net.IPNet{nodes["c"].Subnet},
|
||||||
hostnames: []string{"c"},
|
hostnames: []string{"c"},
|
||||||
@@ -465,6 +514,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
|
||||||
endpoint: nodes["d"].Endpoint,
|
endpoint: nodes["d"].Endpoint,
|
||||||
key: nodes["d"].Key,
|
key: nodes["d"].Key,
|
||||||
|
persistentKeepalive: nodes["d"].PersistentKeepalive,
|
||||||
location: nodeLocationPrefix + nodes["d"].Name,
|
location: nodeLocationPrefix + nodes["d"].Name,
|
||||||
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
cidrs: []*net.IPNet{nodes["d"].Subnet},
|
||||||
hostnames: []string{"d"},
|
hostnames: []string{"d"},
|
||||||
@@ -473,12 +523,13 @@ func TestNewTopology(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
peers: []*Peer{peers["a"], peers["b"]},
|
peers: []*Peer{peers["a"], peers["b"]},
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
tc.result.key = key
|
tc.result.key = key
|
||||||
tc.result.port = port
|
tc.result.port = port
|
||||||
topo, err := NewTopology(nodes, peers, tc.granularity, tc.hostname, port, key, DefaultKiloSubnet, 0)
|
topo, err := NewTopology(nodes, peers, tc.granularity, tc.hostname, port, key, DefaultKiloSubnet, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("test case %q: failed to generate Topology: %v", tc.name, err)
|
t.Errorf("test case %q: failed to generate Topology: %v", tc.name, err)
|
||||||
}
|
}
|
||||||
@@ -489,7 +540,7 @@ func TestNewTopology(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mustTopo(t *testing.T, nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port uint32, key []byte, subnet *net.IPNet, persistentKeepalive int) *Topology {
|
func mustTopo(t *testing.T, nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port uint32, key []byte, subnet *net.IPNet, persistentKeepalive int) *Topology {
|
||||||
topo, err := NewTopology(nodes, peers, granularity, hostname, port, key, subnet, persistentKeepalive)
|
topo, err := NewTopology(nodes, peers, granularity, hostname, port, key, subnet, persistentKeepalive, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to generate Topology: %v", err)
|
t.Errorf("failed to generate Topology: %v", err)
|
||||||
}
|
}
|
||||||
@@ -513,7 +564,7 @@ ListenPort = 51820
|
|||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = key2
|
PublicKey = key2
|
||||||
Endpoint = 10.1.0.2:51820
|
Endpoint = 10.1.0.2:51820
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32, 192.168.178.3/32
|
||||||
PersistentKeepalive = 25
|
PersistentKeepalive = 25
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
@@ -598,7 +649,7 @@ PersistentKeepalive = 25
|
|||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = key2
|
PublicKey = key2
|
||||||
Endpoint = 10.1.0.2:51820
|
Endpoint = 10.1.0.2:51820
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32
|
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32, 192.168.178.3/32
|
||||||
PersistentKeepalive = 25
|
PersistentKeepalive = 25
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
@@ -672,7 +723,7 @@ PersistentKeepalive = 25
|
|||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = key2
|
PublicKey = key2
|
||||||
Endpoint = 10.1.0.2:51820
|
Endpoint = 10.1.0.2:51820
|
||||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32
|
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32, 192.168.178.3/32
|
||||||
|
|
||||||
[Peer]
|
[Peer]
|
||||||
PublicKey = key4
|
PublicKey = key4
|
||||||
@@ -928,3 +979,133 @@ func TestDeduplicatePeerIPs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFilterAllowedIPs(t *testing.T) {
|
||||||
|
nodes, peers, key, port := setup(t)
|
||||||
|
topo := mustTopo(t, nodes, peers, LogicalGranularity, nodes["a"].Name, port, key, DefaultKiloSubnet, nodes["a"].PersistentKeepalive)
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
allowedLocationIPs map[int][]*net.IPNet
|
||||||
|
result map[int][]*net.IPNet
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nothing to filter",
|
||||||
|
allowedLocationIPs: map[int][]*net.IPNet{
|
||||||
|
0: {
|
||||||
|
mustParseCIDR("192.168.178.4/32"),
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
mustParseCIDR("192.168.178.5/32"),
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
mustParseCIDR("192.168.178.6/32"),
|
||||||
|
mustParseCIDR("192.168.178.7/32"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: map[int][]*net.IPNet{
|
||||||
|
0: {
|
||||||
|
mustParseCIDR("192.168.178.4/32"),
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
mustParseCIDR("192.168.178.5/32"),
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
mustParseCIDR("192.168.178.6/32"),
|
||||||
|
mustParseCIDR("192.168.178.7/32"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "intersections between segments",
|
||||||
|
allowedLocationIPs: map[int][]*net.IPNet{
|
||||||
|
0: {
|
||||||
|
mustParseCIDR("192.168.178.4/32"),
|
||||||
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
mustParseCIDR("192.168.178.5/32"),
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
mustParseCIDR("192.168.178.6/32"),
|
||||||
|
mustParseCIDR("192.168.178.7/32"),
|
||||||
|
mustParseCIDR("192.168.178.4/32"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: map[int][]*net.IPNet{
|
||||||
|
0: {
|
||||||
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
mustParseCIDR("192.168.178.5/32"),
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
mustParseCIDR("192.168.178.6/32"),
|
||||||
|
mustParseCIDR("192.168.178.7/32"),
|
||||||
|
mustParseCIDR("192.168.178.4/32"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "intersections with wireGuardCIDR",
|
||||||
|
allowedLocationIPs: map[int][]*net.IPNet{
|
||||||
|
0: {
|
||||||
|
mustParseCIDR("10.4.0.1/32"),
|
||||||
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
mustParseCIDR("192.168.178.5/32"),
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
mustParseCIDR("192.168.178.6/32"),
|
||||||
|
mustParseCIDR("192.168.178.7/32"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: map[int][]*net.IPNet{
|
||||||
|
0: {
|
||||||
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
mustParseCIDR("192.168.178.5/32"),
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
mustParseCIDR("192.168.178.6/32"),
|
||||||
|
mustParseCIDR("192.168.178.7/32"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "intersections with more than one allowedLocationIPs",
|
||||||
|
allowedLocationIPs: map[int][]*net.IPNet{
|
||||||
|
0: {
|
||||||
|
mustParseCIDR("192.168.178.8/32"),
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
mustParseCIDR("192.168.178.5/32"),
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
mustParseCIDR("192.168.178.7/24"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: map[int][]*net.IPNet{
|
||||||
|
0: {},
|
||||||
|
1: {},
|
||||||
|
2: {
|
||||||
|
mustParseCIDR("192.168.178.7/24"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
for k, v := range tc.allowedLocationIPs {
|
||||||
|
topo.segments[k].allowedLocationIPs = v
|
||||||
|
}
|
||||||
|
for k, v := range topo.segments {
|
||||||
|
f := topo.filterAllowedLocationIPs(v.allowedLocationIPs, v.location)
|
||||||
|
// Overwrite the allowedLocationIPs to mimic the actual usage of the filterAllowedLocationIPs function.
|
||||||
|
topo.segments[k].allowedLocationIPs = f
|
||||||
|
if !ipNetSlicesEqual(f, tc.result[k]) {
|
||||||
|
t.Errorf("test case %q:\n\texpected:\n\t%q\n\tgot:\n\t%q\n", tc.name, tc.result[k], f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -108,14 +108,14 @@ func (t *Table) Run(stop <-chan struct{}) (<-chan error, error) {
|
|||||||
switch e.Type {
|
switch e.Type {
|
||||||
// Watch for deleted routes to reconcile this table's routes.
|
// Watch for deleted routes to reconcile this table's routes.
|
||||||
case unix.RTM_DELROUTE:
|
case unix.RTM_DELROUTE:
|
||||||
|
// Filter out invalid routes.
|
||||||
|
if e.Route.Dst == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
for k := range t.rs {
|
for k := range t.rs {
|
||||||
switch r := t.rs[k].(type) {
|
switch r := t.rs[k].(type) {
|
||||||
case *netlink.Route:
|
case *netlink.Route:
|
||||||
// Filter out invalid routes.
|
|
||||||
if r == nil || r.Dst == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// If any deleted route's destination matches a destination
|
// If any deleted route's destination matches a destination
|
||||||
// in the table, reset the corresponding route just in case.
|
// in the table, reset the corresponding route just in case.
|
||||||
if r.Dst.IP.Equal(e.Route.Dst.IP) && r.Dst.Mask.String() == e.Route.Dst.Mask.String() {
|
if r.Dst.IP.Equal(e.Route.Dst.IP) && r.Dst.Mask.String() == e.Route.Dst.Mask.String() {
|
||||||
|
@@ -17,11 +17,13 @@ package wireguard
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation"
|
"k8s.io/apimachinery/pkg/util/validation"
|
||||||
)
|
)
|
||||||
@@ -31,6 +33,9 @@ type key string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
separator = "="
|
separator = "="
|
||||||
|
dumpSeparator = "\t"
|
||||||
|
dumpNone = "(none)"
|
||||||
|
dumpOff = "off"
|
||||||
interfaceSection section = "Interface"
|
interfaceSection section = "Interface"
|
||||||
peerSection section = "Peer"
|
peerSection section = "Peer"
|
||||||
listenPortKey key = "ListenPort"
|
listenPortKey key = "ListenPort"
|
||||||
@@ -42,6 +47,30 @@ const (
|
|||||||
publicKeyKey key = "PublicKey"
|
publicKeyKey key = "PublicKey"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type dumpInterfaceIndex int
|
||||||
|
|
||||||
|
const (
|
||||||
|
dumpInterfacePrivateKeyIndex = iota
|
||||||
|
dumpInterfacePublicKeyIndex
|
||||||
|
dumpInterfaceListenPortIndex
|
||||||
|
dumpInterfaceFWMarkIndex
|
||||||
|
dumpInterfaceLen
|
||||||
|
)
|
||||||
|
|
||||||
|
type dumpPeerIndex int
|
||||||
|
|
||||||
|
const (
|
||||||
|
dumpPeerPublicKeyIndex = iota
|
||||||
|
dumpPeerPresharedKeyIndex
|
||||||
|
dumpPeerEndpointIndex
|
||||||
|
dumpPeerAllowedIPsIndex
|
||||||
|
dumpPeerLatestHandshakeIndex
|
||||||
|
dumpPeerTransferRXIndex
|
||||||
|
dumpPeerTransferTXIndex
|
||||||
|
dumpPeerPersistentKeepaliveIndex
|
||||||
|
dumpPeerLen
|
||||||
|
)
|
||||||
|
|
||||||
// Conf represents a WireGuard configuration file.
|
// Conf represents a WireGuard configuration file.
|
||||||
type Conf struct {
|
type Conf struct {
|
||||||
Interface *Interface
|
Interface *Interface
|
||||||
@@ -61,6 +90,8 @@ type Peer struct {
|
|||||||
PersistentKeepalive int
|
PersistentKeepalive int
|
||||||
PresharedKey []byte
|
PresharedKey []byte
|
||||||
PublicKey []byte
|
PublicKey []byte
|
||||||
|
// The following fields are part of the runtime information, not the configuration.
|
||||||
|
LatestHandshake time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeduplicateIPs eliminates duplicate allowed IPs.
|
// DeduplicateIPs eliminates duplicate allowed IPs.
|
||||||
@@ -95,6 +126,38 @@ func (e *Endpoint) String() string {
|
|||||||
return dnsOrIP + ":" + strconv.FormatUint(uint64(e.Port), 10)
|
return dnsOrIP + ":" + strconv.FormatUint(uint64(e.Port), 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equal compares two endpoints.
|
||||||
|
func (e *Endpoint) Equal(b *Endpoint, DNSFirst bool) bool {
|
||||||
|
if (e == nil) != (b == nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e != nil {
|
||||||
|
if e.Port != b.Port {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if DNSFirst {
|
||||||
|
// Check the DNS name first if it was resolved.
|
||||||
|
if e.DNS != b.DNS {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e.DNS == "" && !e.IP.Equal(b.IP) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// IPs take priority, so check them first.
|
||||||
|
if !e.IP.Equal(b.IP) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Only check the DNS name if the IP is empty.
|
||||||
|
if e.IP == nil && e.DNS != b.DNS {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// DNSOrIP represents either a DNS name or an IP address.
|
// DNSOrIP represents either a DNS name or an IP address.
|
||||||
// IPs, as they are more specific, are preferred.
|
// IPs, as they are more specific, are preferred.
|
||||||
type DNSOrIP struct {
|
type DNSOrIP struct {
|
||||||
@@ -114,13 +177,11 @@ func (d DNSOrIP) String() string {
|
|||||||
func Parse(buf []byte) *Conf {
|
func Parse(buf []byte) *Conf {
|
||||||
var (
|
var (
|
||||||
active section
|
active section
|
||||||
ai *net.IPNet
|
|
||||||
kv []string
|
kv []string
|
||||||
c Conf
|
c Conf
|
||||||
err error
|
err error
|
||||||
iface *Interface
|
iface *Interface
|
||||||
i int
|
i int
|
||||||
ip, ip4 net.IP
|
|
||||||
k key
|
k key
|
||||||
line, v string
|
line, v string
|
||||||
peer *Peer
|
peer *Peer
|
||||||
@@ -173,49 +234,15 @@ func Parse(buf []byte) *Conf {
|
|||||||
case peerSection:
|
case peerSection:
|
||||||
switch k {
|
switch k {
|
||||||
case allowedIPsKey:
|
case allowedIPsKey:
|
||||||
// Reuse string slice.
|
err = peer.parseAllowedIPs(v)
|
||||||
kv = strings.Split(v, ",")
|
|
||||||
for i = range kv {
|
|
||||||
ip, ai, err = net.ParseCIDR(strings.TrimSpace(kv[i]))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if ip4 = ip.To4(); ip4 != nil {
|
|
||||||
ip = ip4
|
|
||||||
} else {
|
|
||||||
ip = ip.To16()
|
|
||||||
}
|
|
||||||
ai.IP = ip
|
|
||||||
peer.AllowedIPs = append(peer.AllowedIPs, ai)
|
|
||||||
}
|
|
||||||
case endpointKey:
|
case endpointKey:
|
||||||
// Reuse string slice.
|
err = peer.parseEndpoint(v)
|
||||||
kv = strings.Split(v, ":")
|
|
||||||
if len(kv) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
port, err = strconv.ParseUint(kv[len(kv)-1], 10, 32)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
d := DNSOrIP{}
|
|
||||||
ip = net.ParseIP(strings.Trim(strings.Join(kv[:len(kv)-1], ":"), "[]"))
|
|
||||||
if ip == nil {
|
|
||||||
if len(validation.IsDNS1123Subdomain(kv[0])) != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
d.DNS = kv[0]
|
|
||||||
} else {
|
|
||||||
if ip4 = ip.To4(); ip4 != nil {
|
|
||||||
d.IP = ip4
|
|
||||||
} else {
|
|
||||||
d.IP = ip.To16()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
peer.Endpoint = &Endpoint{
|
|
||||||
DNSOrIP: d,
|
|
||||||
Port: uint32(port),
|
|
||||||
}
|
|
||||||
case persistentKeepaliveKey:
|
case persistentKeepaliveKey:
|
||||||
i, err = strconv.Atoi(v)
|
i, err = strconv.Atoi(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -309,22 +336,9 @@ func (c *Conf) Equal(b *Conf) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c.Peers[i].Endpoint == nil) != (b.Peers[i].Endpoint == nil) {
|
if !c.Peers[i].Endpoint.Equal(b.Peers[i].Endpoint, false) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if c.Peers[i].Endpoint != nil {
|
|
||||||
if c.Peers[i].Endpoint.Port != b.Peers[i].Endpoint.Port {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// IPs take priority, so check them first.
|
|
||||||
if !c.Peers[i].Endpoint.IP.Equal(b.Peers[i].Endpoint.IP) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Only check the DNS name if the IP is empty.
|
|
||||||
if c.Peers[i].Endpoint.IP == nil && c.Peers[i].Endpoint.DNS != b.Peers[i].Endpoint.DNS {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.Peers[i].PersistentKeepalive != b.Peers[i].PersistentKeepalive || !bytes.Equal(c.Peers[i].PresharedKey, b.Peers[i].PresharedKey) || !bytes.Equal(c.Peers[i].PublicKey, b.Peers[i].PublicKey) {
|
if c.Peers[i].PersistentKeepalive != b.Peers[i].PersistentKeepalive || !bytes.Equal(c.Peers[i].PresharedKey, b.Peers[i].PresharedKey) || !bytes.Equal(c.Peers[i].PublicKey, b.Peers[i].PublicKey) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -429,3 +443,177 @@ func writeKey(buf *bytes.Buffer, k key) error {
|
|||||||
_, err = buf.WriteString(" = ")
|
_, err = buf.WriteString(" = ")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errParseEndpoint = errors.New("could not parse Endpoint")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Peer) parseEndpoint(v string) error {
|
||||||
|
var (
|
||||||
|
kv []string
|
||||||
|
err error
|
||||||
|
ip, ip4 net.IP
|
||||||
|
port uint64
|
||||||
|
)
|
||||||
|
kv = strings.Split(v, ":")
|
||||||
|
if len(kv) < 2 {
|
||||||
|
return errParseEndpoint
|
||||||
|
}
|
||||||
|
port, err = strconv.ParseUint(kv[len(kv)-1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d := DNSOrIP{}
|
||||||
|
ip = net.ParseIP(strings.Trim(strings.Join(kv[:len(kv)-1], ":"), "[]"))
|
||||||
|
if ip == nil {
|
||||||
|
if len(validation.IsDNS1123Subdomain(kv[0])) != 0 {
|
||||||
|
return errParseEndpoint
|
||||||
|
}
|
||||||
|
d.DNS = kv[0]
|
||||||
|
} else {
|
||||||
|
if ip4 = ip.To4(); ip4 != nil {
|
||||||
|
d.IP = ip4
|
||||||
|
} else {
|
||||||
|
d.IP = ip.To16()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Endpoint = &Endpoint{
|
||||||
|
DNSOrIP: d,
|
||||||
|
Port: uint32(port),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Peer) parseAllowedIPs(v string) error {
|
||||||
|
var (
|
||||||
|
ai *net.IPNet
|
||||||
|
kv []string
|
||||||
|
err error
|
||||||
|
i int
|
||||||
|
ip, ip4 net.IP
|
||||||
|
)
|
||||||
|
|
||||||
|
kv = strings.Split(v, ",")
|
||||||
|
for i = range kv {
|
||||||
|
ip, ai, err = net.ParseCIDR(strings.TrimSpace(kv[i]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ip4 = ip.To4(); ip4 != nil {
|
||||||
|
ip = ip4
|
||||||
|
} else {
|
||||||
|
ip = ip.To16()
|
||||||
|
}
|
||||||
|
ai.IP = ip
|
||||||
|
p.AllowedIPs = append(p.AllowedIPs, ai)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseDump parses a given WireGuard dump and produces a Conf struct.
|
||||||
|
func ParseDump(buf []byte) (*Conf, error) {
|
||||||
|
// from man wg, show section:
|
||||||
|
// If dump is specified, then several lines are printed;
|
||||||
|
// the first contains in order separated by tab: private-key, public-key, listen-port, fw‐mark.
|
||||||
|
// Subsequent lines are printed for each peer and contain in order separated by tab:
|
||||||
|
// public-key, preshared-key, endpoint, allowed-ips, latest-handshake, transfer-rx, transfer-tx, persistent-keepalive.
|
||||||
|
var (
|
||||||
|
active section
|
||||||
|
values []string
|
||||||
|
c Conf
|
||||||
|
err error
|
||||||
|
iface *Interface
|
||||||
|
peer *Peer
|
||||||
|
port uint64
|
||||||
|
sec int64
|
||||||
|
pka int
|
||||||
|
line int
|
||||||
|
)
|
||||||
|
// First line is Interface
|
||||||
|
active = interfaceSection
|
||||||
|
s := bufio.NewScanner(bytes.NewBuffer(buf))
|
||||||
|
for s.Scan() {
|
||||||
|
values = strings.Split(s.Text(), dumpSeparator)
|
||||||
|
|
||||||
|
switch active {
|
||||||
|
case interfaceSection:
|
||||||
|
if len(values) < dumpInterfaceLen {
|
||||||
|
return nil, fmt.Errorf("invalid interface line: missing fields (%d < %d)", len(values), dumpInterfaceLen)
|
||||||
|
}
|
||||||
|
iface = new(Interface)
|
||||||
|
for i := range values {
|
||||||
|
switch i {
|
||||||
|
case dumpInterfacePrivateKeyIndex:
|
||||||
|
iface.PrivateKey = []byte(values[i])
|
||||||
|
case dumpInterfaceListenPortIndex:
|
||||||
|
port, err = strconv.ParseUint(values[i], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid interface line: error parsing listen-port: %w", err)
|
||||||
|
}
|
||||||
|
iface.ListenPort = uint32(port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Interface = iface
|
||||||
|
// Next lines are Peers
|
||||||
|
active = peerSection
|
||||||
|
case peerSection:
|
||||||
|
if len(values) < dumpPeerLen {
|
||||||
|
return nil, fmt.Errorf("invalid peer line %d: missing fields (%d < %d)", line, len(values), dumpPeerLen)
|
||||||
|
}
|
||||||
|
peer = new(Peer)
|
||||||
|
|
||||||
|
for i := range values {
|
||||||
|
switch i {
|
||||||
|
case dumpPeerPublicKeyIndex:
|
||||||
|
peer.PublicKey = []byte(values[i])
|
||||||
|
case dumpPeerPresharedKeyIndex:
|
||||||
|
if values[i] == dumpNone {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
peer.PresharedKey = []byte(values[i])
|
||||||
|
case dumpPeerEndpointIndex:
|
||||||
|
if values[i] == dumpNone {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = peer.parseEndpoint(values[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid peer line %d: error parsing endpoint: %w", line, err)
|
||||||
|
}
|
||||||
|
case dumpPeerAllowedIPsIndex:
|
||||||
|
if values[i] == dumpNone {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = peer.parseAllowedIPs(values[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid peer line %d: error parsing allowed-ips: %w", line, err)
|
||||||
|
}
|
||||||
|
case dumpPeerLatestHandshakeIndex:
|
||||||
|
if values[i] == "0" {
|
||||||
|
// Use go zero value, not unix 0 timestamp.
|
||||||
|
peer.LatestHandshake = time.Time{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sec, err = strconv.ParseInt(values[i], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid peer line %d: error parsing latest-handshake: %w", line, err)
|
||||||
|
}
|
||||||
|
peer.LatestHandshake = time.Unix(sec, 0)
|
||||||
|
case dumpPeerPersistentKeepaliveIndex:
|
||||||
|
if values[i] == dumpOff {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pka, err = strconv.Atoi(values[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid peer line %d: error parsing persistent-keepalive: %w", line, err)
|
||||||
|
}
|
||||||
|
peer.PersistentKeepalive = pka
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Peers = append(c.Peers, peer)
|
||||||
|
peer = nil
|
||||||
|
}
|
||||||
|
line++
|
||||||
|
}
|
||||||
|
return &c, nil
|
||||||
|
}
|
||||||
|
@@ -15,7 +15,10 @@
|
|||||||
package wireguard
|
package wireguard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kylelemons/godebug/pretty"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCompareConf(t *testing.T) {
|
func TestCompareConf(t *testing.T) {
|
||||||
@@ -203,3 +206,151 @@ func TestCompareConf(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCompareEndpoint(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
a *Endpoint
|
||||||
|
b *Endpoint
|
||||||
|
dnsFirst bool
|
||||||
|
out bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "both nil",
|
||||||
|
a: nil,
|
||||||
|
b: nil,
|
||||||
|
out: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "a nil",
|
||||||
|
a: nil,
|
||||||
|
b: &Endpoint{},
|
||||||
|
out: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "b nil",
|
||||||
|
a: &Endpoint{},
|
||||||
|
b: nil,
|
||||||
|
out: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "zero",
|
||||||
|
a: &Endpoint{},
|
||||||
|
b: &Endpoint{},
|
||||||
|
out: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "diff port",
|
||||||
|
a: &Endpoint{Port: 1234},
|
||||||
|
b: &Endpoint{Port: 5678},
|
||||||
|
out: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same IP",
|
||||||
|
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1")}},
|
||||||
|
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1")}},
|
||||||
|
out: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "diff IP",
|
||||||
|
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1")}},
|
||||||
|
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.2")}},
|
||||||
|
out: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same IP ignore DNS",
|
||||||
|
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: "a"}},
|
||||||
|
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: "b"}},
|
||||||
|
out: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no IP check DNS",
|
||||||
|
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "a"}},
|
||||||
|
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "b"}},
|
||||||
|
out: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no IP check DNS (same)",
|
||||||
|
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "a"}},
|
||||||
|
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "a"}},
|
||||||
|
out: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DNS first, ignore IP",
|
||||||
|
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: "a"}},
|
||||||
|
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.2"), DNS: "a"}},
|
||||||
|
dnsFirst: true,
|
||||||
|
out: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DNS first",
|
||||||
|
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "a"}},
|
||||||
|
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{DNS: "b"}},
|
||||||
|
dnsFirst: true,
|
||||||
|
out: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DNS first, no DNS compare IP",
|
||||||
|
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: ""}},
|
||||||
|
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.2"), DNS: ""}},
|
||||||
|
dnsFirst: true,
|
||||||
|
out: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DNS first, no DNS compare IP (same)",
|
||||||
|
a: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: ""}},
|
||||||
|
b: &Endpoint{Port: 1234, DNSOrIP: DNSOrIP{IP: net.ParseIP("192.168.0.1"), DNS: ""}},
|
||||||
|
dnsFirst: true,
|
||||||
|
out: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
equal := tc.a.Equal(tc.b, tc.dnsFirst)
|
||||||
|
if equal != tc.out {
|
||||||
|
t.Errorf("test case %q: expected %t, got %t", tc.name, tc.out, equal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareDumpConf(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
d []byte
|
||||||
|
c []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
d: []byte{},
|
||||||
|
c: []byte{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "redacted copy from wg output",
|
||||||
|
d: []byte(`private B7qk8EMlob0nfado0ABM6HulUV607r4yqtBKjhap7S4= 51820 off
|
||||||
|
key1 (none) 10.254.1.1:51820 100.64.1.0/24,192.168.0.125/32,10.4.0.1/32 1619012801 67048 34952 10
|
||||||
|
key2 (none) 10.254.2.1:51820 100.64.4.0/24,10.69.76.55/32,100.64.3.0/24,10.66.25.131/32,10.4.0.2/32 1619013058 1134456 10077852 10`),
|
||||||
|
c: []byte(`[Interface]
|
||||||
|
ListenPort = 51820
|
||||||
|
PrivateKey = private
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = key1
|
||||||
|
AllowedIPs = 100.64.1.0/24, 192.168.0.125/32, 10.4.0.1/32
|
||||||
|
Endpoint = 10.254.1.1:51820
|
||||||
|
PersistentKeepalive = 10
|
||||||
|
|
||||||
|
[Peer]
|
||||||
|
PublicKey = key2
|
||||||
|
AllowedIPs = 100.64.4.0/24, 10.69.76.55/32, 100.64.3.0/24, 10.66.25.131/32, 10.4.0.2/32
|
||||||
|
Endpoint = 10.254.2.1:51820
|
||||||
|
PersistentKeepalive = 10`),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
|
||||||
|
dumpConf, _ := ParseDump(tc.d)
|
||||||
|
conf := Parse(tc.c)
|
||||||
|
// Equal will ignore runtime fields and only compare configuration fields.
|
||||||
|
if !dumpConf.Equal(conf) {
|
||||||
|
diff := pretty.Compare(dumpConf, conf)
|
||||||
|
t.Errorf("test case %q: got diff: %v", tc.name, diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -119,3 +119,15 @@ func ShowConf(iface string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return stdout.Bytes(), nil
|
return stdout.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShowDump gets the WireGuard configuration and runtime information for the given interface.
|
||||||
|
func ShowDump(iface string) ([]byte, error) {
|
||||||
|
cmd := exec.Command("wg", "show", iface, "dump")
|
||||||
|
var stderr, stdout bytes.Buffer
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read the WireGuard dump output: %s", stderr.String())
|
||||||
|
}
|
||||||
|
return stdout.Bytes(), nil
|
||||||
|
}
|
||||||
|
2
tools.go
2
tools.go
@@ -23,5 +23,5 @@ import (
|
|||||||
_ "k8s.io/code-generator/cmd/deepcopy-gen"
|
_ "k8s.io/code-generator/cmd/deepcopy-gen"
|
||||||
_ "k8s.io/code-generator/cmd/informer-gen"
|
_ "k8s.io/code-generator/cmd/informer-gen"
|
||||||
_ "k8s.io/code-generator/cmd/lister-gen"
|
_ "k8s.io/code-generator/cmd/lister-gen"
|
||||||
_ "k8s.io/kube-openapi/cmd/openapi-gen"
|
_ "sigs.k8s.io/controller-tools/cmd/controller-gen"
|
||||||
)
|
)
|
||||||
|
5
vendor/github.com/PuerkitoBio/purell/.gitignore
generated
vendored
5
vendor/github.com/PuerkitoBio/purell/.gitignore
generated
vendored
@@ -1,5 +0,0 @@
|
|||||||
*.sublime-*
|
|
||||||
.DS_Store
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
tags
|
|
12
vendor/github.com/PuerkitoBio/purell/.travis.yml
generated
vendored
12
vendor/github.com/PuerkitoBio/purell/.travis.yml
generated
vendored
@@ -1,12 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.4.x
|
|
||||||
- 1.5.x
|
|
||||||
- 1.6.x
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
|
||||||
- "1.10.x"
|
|
||||||
- "1.11.x"
|
|
||||||
- tip
|
|
12
vendor/github.com/PuerkitoBio/purell/LICENSE
generated
vendored
12
vendor/github.com/PuerkitoBio/purell/LICENSE
generated
vendored
@@ -1,12 +0,0 @@
|
|||||||
Copyright (c) 2012, Martin Angers
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
188
vendor/github.com/PuerkitoBio/purell/README.md
generated
vendored
188
vendor/github.com/PuerkitoBio/purell/README.md
generated
vendored
@@ -1,188 +0,0 @@
|
|||||||
# Purell
|
|
||||||
|
|
||||||
Purell is a tiny Go library to normalize URLs. It returns a pure URL. Pure-ell. Sanitizer and all. Yeah, I know...
|
|
||||||
|
|
||||||
Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc].
|
|
||||||
|
|
||||||
[](http://travis-ci.org/PuerkitoBio/purell)
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
`go get github.com/PuerkitoBio/purell`
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
* **v1.1.1** : Fix failing test due to Go1.12 changes (thanks to @ianlancetaylor).
|
|
||||||
* **2016-11-14 (v1.1.0)** : IDN: Conform to RFC 5895: Fold character width (thanks to @beeker1121).
|
|
||||||
* **2016-07-27 (v1.0.0)** : Normalize IDN to ASCII (thanks to @zenovich).
|
|
||||||
* **2015-02-08** : Add fix for relative paths issue ([PR #5][pr5]) and add fix for unnecessary encoding of reserved characters ([see issue #7][iss7]).
|
|
||||||
* **v0.2.0** : Add benchmarks, Attempt IDN support.
|
|
||||||
* **v0.1.0** : Initial release.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
From `example_test.go` (note that in your code, you would import "github.com/PuerkitoBio/purell", and would prefix references to its methods and constants with "purell."):
|
|
||||||
|
|
||||||
```go
|
|
||||||
package purell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleNormalizeURLString() {
|
|
||||||
if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/",
|
|
||||||
FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else {
|
|
||||||
fmt.Print(normalized)
|
|
||||||
}
|
|
||||||
// Output: http://somewebsite.com:80/Amazing%3F/url/
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleMustNormalizeURLString() {
|
|
||||||
normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/",
|
|
||||||
FlagsUnsafeGreedy)
|
|
||||||
fmt.Print(normalized)
|
|
||||||
|
|
||||||
// Output: http://somewebsite.com/Amazing%FA/url
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleNormalizeURL() {
|
|
||||||
if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else {
|
|
||||||
normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment)
|
|
||||||
fmt.Print(normalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
As seen in the examples above, purell offers three methods, `NormalizeURLString(string, NormalizationFlags) (string, error)`, `MustNormalizeURLString(string, NormalizationFlags) (string)` and `NormalizeURL(*url.URL, NormalizationFlags) (string)`. They all normalize the provided URL based on the specified flags. Here are the available flags:
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
// Safe normalizations
|
|
||||||
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
|
|
||||||
FlagLowercaseHost // http://HOST -> http://host
|
|
||||||
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
|
|
||||||
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
|
|
||||||
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
|
|
||||||
FlagRemoveDefaultPort // http://host:80 -> http://host
|
|
||||||
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
|
|
||||||
|
|
||||||
// Usually safe normalizations
|
|
||||||
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
|
|
||||||
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
|
|
||||||
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
|
|
||||||
|
|
||||||
// Unsafe normalizations
|
|
||||||
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
|
|
||||||
FlagRemoveFragment // http://host/path#fragment -> http://host/path
|
|
||||||
FlagForceHTTP // https://host -> http://host
|
|
||||||
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
|
|
||||||
FlagRemoveWWW // http://www.host/ -> http://host/
|
|
||||||
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
|
|
||||||
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
|
|
||||||
|
|
||||||
// Normalizations not in the wikipedia article, required to cover tests cases
|
|
||||||
// submitted by jehiah
|
|
||||||
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
|
|
||||||
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
|
|
||||||
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
|
|
||||||
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
|
|
||||||
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
|
|
||||||
|
|
||||||
// Convenience set of safe normalizations
|
|
||||||
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
|
|
||||||
|
|
||||||
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
|
|
||||||
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
|
|
||||||
|
|
||||||
// Convenience set of usually safe normalizations (includes FlagsSafe)
|
|
||||||
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
|
|
||||||
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
|
|
||||||
|
|
||||||
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
|
|
||||||
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
|
|
||||||
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
|
|
||||||
|
|
||||||
// Convenience set of all available flags
|
|
||||||
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
|
||||||
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
For convenience, the set of flags `FlagsSafe`, `FlagsUsuallySafe[Greedy|NonGreedy]`, `FlagsUnsafe[Greedy|NonGreedy]` and `FlagsAll[Greedy|NonGreedy]` are provided for the similarly grouped normalizations on [wikipedia's URL normalization page][wiki]. You can add (using the bitwise OR `|` operator) or remove (using the bitwise AND NOT `&^` operator) individual flags from the sets if required, to build your own custom set.
|
|
||||||
|
|
||||||
The [full godoc reference is available on gopkgdoc][godoc].
|
|
||||||
|
|
||||||
Some things to note:
|
|
||||||
|
|
||||||
* `FlagDecodeUnnecessaryEscapes`, `FlagEncodeNecessaryEscapes`, `FlagUppercaseEscapes` and `FlagRemoveEmptyQuerySeparator` are always implicitly set, because internally, the URL string is parsed as an URL object, which automatically decodes unnecessary escapes, uppercases and encodes necessary ones, and removes empty query separators (an unnecessary `?` at the end of the url). So this operation cannot **not** be done. For this reason, `FlagRemoveEmptyQuerySeparator` (as well as the other three) has been included in the `FlagsSafe` convenience set, instead of `FlagsUnsafe`, where Wikipedia puts it.
|
|
||||||
|
|
||||||
* The `FlagDecodeUnnecessaryEscapes` decodes the following escapes (*from -> to*):
|
|
||||||
- %24 -> $
|
|
||||||
- %26 -> &
|
|
||||||
- %2B-%3B -> +,-./0123456789:;
|
|
||||||
- %3D -> =
|
|
||||||
- %40-%5A -> @ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
|
||||||
- %5F -> _
|
|
||||||
- %61-%7A -> abcdefghijklmnopqrstuvwxyz
|
|
||||||
- %7E -> ~
|
|
||||||
|
|
||||||
|
|
||||||
* When the `NormalizeURL` function is used (passing an URL object), this source URL object is modified (that is, after the call, the URL object will be modified to reflect the normalization).
|
|
||||||
|
|
||||||
* The *replace IP with domain name* normalization (`http://208.77.188.166/ → http://www.example.com/`) is obviously not possible for a library without making some network requests. This is not implemented in purell.
|
|
||||||
|
|
||||||
* The *remove unused query string parameters* and *remove default query parameters* are also not implemented, since this is a very case-specific normalization, and it is quite trivial to do with an URL object.
|
|
||||||
|
|
||||||
### Safe vs Usually Safe vs Unsafe
|
|
||||||
|
|
||||||
Purell allows you to control the level of risk you take while normalizing an URL. You can aggressively normalize, play it totally safe, or anything in between.
|
|
||||||
|
|
||||||
Consider the following URL:
|
|
||||||
|
|
||||||
`HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
|
||||||
|
|
||||||
Normalizing with the `FlagsSafe` gives:
|
|
||||||
|
|
||||||
`https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
|
||||||
|
|
||||||
With the `FlagsUsuallySafeGreedy`:
|
|
||||||
|
|
||||||
`https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid`
|
|
||||||
|
|
||||||
And with `FlagsUnsafeGreedy`:
|
|
||||||
|
|
||||||
`http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3`
|
|
||||||
|
|
||||||
## TODOs
|
|
||||||
|
|
||||||
* Add a class/default instance to allow specifying custom directory index names? At the moment, removing directory index removes `(^|/)((?:default|index)\.\w{1,4})$`.
|
|
||||||
|
|
||||||
## Thanks / Contributions
|
|
||||||
|
|
||||||
@rogpeppe
|
|
||||||
@jehiah
|
|
||||||
@opennota
|
|
||||||
@pchristopher1275
|
|
||||||
@zenovich
|
|
||||||
@beeker1121
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The [BSD 3-Clause license][bsd].
|
|
||||||
|
|
||||||
[bsd]: http://opensource.org/licenses/BSD-3-Clause
|
|
||||||
[wiki]: http://en.wikipedia.org/wiki/URL_normalization
|
|
||||||
[rfc]: http://tools.ietf.org/html/rfc3986#section-6
|
|
||||||
[godoc]: http://go.pkgdoc.org/github.com/PuerkitoBio/purell
|
|
||||||
[pr5]: https://github.com/PuerkitoBio/purell/pull/5
|
|
||||||
[iss7]: https://github.com/PuerkitoBio/purell/issues/7
|
|
379
vendor/github.com/PuerkitoBio/purell/purell.go
generated
vendored
379
vendor/github.com/PuerkitoBio/purell/purell.go
generated
vendored
@@ -1,379 +0,0 @@
|
|||||||
/*
|
|
||||||
Package purell offers URL normalization as described on the wikipedia page:
|
|
||||||
http://en.wikipedia.org/wiki/URL_normalization
|
|
||||||
*/
|
|
||||||
package purell
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/PuerkitoBio/urlesc"
|
|
||||||
"golang.org/x/net/idna"
|
|
||||||
"golang.org/x/text/unicode/norm"
|
|
||||||
"golang.org/x/text/width"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A set of normalization flags determines how a URL will
|
|
||||||
// be normalized.
|
|
||||||
type NormalizationFlags uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Safe normalizations
|
|
||||||
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
|
|
||||||
FlagLowercaseHost // http://HOST -> http://host
|
|
||||||
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
|
|
||||||
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
|
|
||||||
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
|
|
||||||
FlagRemoveDefaultPort // http://host:80 -> http://host
|
|
||||||
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
|
|
||||||
|
|
||||||
// Usually safe normalizations
|
|
||||||
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
|
|
||||||
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
|
|
||||||
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
|
|
||||||
|
|
||||||
// Unsafe normalizations
|
|
||||||
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
|
|
||||||
FlagRemoveFragment // http://host/path#fragment -> http://host/path
|
|
||||||
FlagForceHTTP // https://host -> http://host
|
|
||||||
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
|
|
||||||
FlagRemoveWWW // http://www.host/ -> http://host/
|
|
||||||
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
|
|
||||||
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
|
|
||||||
|
|
||||||
// Normalizations not in the wikipedia article, required to cover tests cases
|
|
||||||
// submitted by jehiah
|
|
||||||
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
|
|
||||||
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
|
|
||||||
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
|
|
||||||
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
|
|
||||||
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
|
|
||||||
|
|
||||||
// Convenience set of safe normalizations
|
|
||||||
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
|
|
||||||
|
|
||||||
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
|
|
||||||
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
|
|
||||||
|
|
||||||
// Convenience set of usually safe normalizations (includes FlagsSafe)
|
|
||||||
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
|
|
||||||
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
|
|
||||||
|
|
||||||
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
|
|
||||||
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
|
|
||||||
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
|
|
||||||
|
|
||||||
// Convenience set of all available flags
|
|
||||||
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
|
||||||
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultHttpPort = ":80"
|
|
||||||
defaultHttpsPort = ":443"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Regular expressions used by the normalizations
|
|
||||||
var rxPort = regexp.MustCompile(`(:\d+)/?$`)
|
|
||||||
var rxDirIndex = regexp.MustCompile(`(^|/)((?:default|index)\.\w{1,4})$`)
|
|
||||||
var rxDupSlashes = regexp.MustCompile(`/{2,}`)
|
|
||||||
var rxDWORDHost = regexp.MustCompile(`^(\d+)((?:\.+)?(?:\:\d*)?)$`)
|
|
||||||
var rxOctalHost = regexp.MustCompile(`^(0\d*)\.(0\d*)\.(0\d*)\.(0\d*)((?:\.+)?(?:\:\d*)?)$`)
|
|
||||||
var rxHexHost = regexp.MustCompile(`^0x([0-9A-Fa-f]+)((?:\.+)?(?:\:\d*)?)$`)
|
|
||||||
var rxHostDots = regexp.MustCompile(`^(.+?)(:\d+)?$`)
|
|
||||||
var rxEmptyPort = regexp.MustCompile(`:+$`)
|
|
||||||
|
|
||||||
// Map of flags to implementation function.
|
|
||||||
// FlagDecodeUnnecessaryEscapes has no action, since it is done automatically
|
|
||||||
// by parsing the string as an URL. Same for FlagUppercaseEscapes and FlagRemoveEmptyQuerySeparator.
|
|
||||||
|
|
||||||
// Since maps have undefined traversing order, make a slice of ordered keys
|
|
||||||
var flagsOrder = []NormalizationFlags{
|
|
||||||
FlagLowercaseScheme,
|
|
||||||
FlagLowercaseHost,
|
|
||||||
FlagRemoveDefaultPort,
|
|
||||||
FlagRemoveDirectoryIndex,
|
|
||||||
FlagRemoveDotSegments,
|
|
||||||
FlagRemoveFragment,
|
|
||||||
FlagForceHTTP, // Must be after remove default port (because https=443/http=80)
|
|
||||||
FlagRemoveDuplicateSlashes,
|
|
||||||
FlagRemoveWWW,
|
|
||||||
FlagAddWWW,
|
|
||||||
FlagSortQuery,
|
|
||||||
FlagDecodeDWORDHost,
|
|
||||||
FlagDecodeOctalHost,
|
|
||||||
FlagDecodeHexHost,
|
|
||||||
FlagRemoveUnnecessaryHostDots,
|
|
||||||
FlagRemoveEmptyPortSeparator,
|
|
||||||
FlagRemoveTrailingSlash, // These two (add/remove trailing slash) must be last
|
|
||||||
FlagAddTrailingSlash,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... and then the map, where order is unimportant
|
|
||||||
var flags = map[NormalizationFlags]func(*url.URL){
|
|
||||||
FlagLowercaseScheme: lowercaseScheme,
|
|
||||||
FlagLowercaseHost: lowercaseHost,
|
|
||||||
FlagRemoveDefaultPort: removeDefaultPort,
|
|
||||||
FlagRemoveDirectoryIndex: removeDirectoryIndex,
|
|
||||||
FlagRemoveDotSegments: removeDotSegments,
|
|
||||||
FlagRemoveFragment: removeFragment,
|
|
||||||
FlagForceHTTP: forceHTTP,
|
|
||||||
FlagRemoveDuplicateSlashes: removeDuplicateSlashes,
|
|
||||||
FlagRemoveWWW: removeWWW,
|
|
||||||
FlagAddWWW: addWWW,
|
|
||||||
FlagSortQuery: sortQuery,
|
|
||||||
FlagDecodeDWORDHost: decodeDWORDHost,
|
|
||||||
FlagDecodeOctalHost: decodeOctalHost,
|
|
||||||
FlagDecodeHexHost: decodeHexHost,
|
|
||||||
FlagRemoveUnnecessaryHostDots: removeUnncessaryHostDots,
|
|
||||||
FlagRemoveEmptyPortSeparator: removeEmptyPortSeparator,
|
|
||||||
FlagRemoveTrailingSlash: removeTrailingSlash,
|
|
||||||
FlagAddTrailingSlash: addTrailingSlash,
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustNormalizeURLString returns the normalized string, and panics if an error occurs.
|
|
||||||
// It takes an URL string as input, as well as the normalization flags.
|
|
||||||
func MustNormalizeURLString(u string, f NormalizationFlags) string {
|
|
||||||
result, e := NormalizeURLString(u, f)
|
|
||||||
if e != nil {
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalizeURLString returns the normalized string, or an error if it can't be parsed into an URL object.
|
|
||||||
// It takes an URL string as input, as well as the normalization flags.
|
|
||||||
func NormalizeURLString(u string, f NormalizationFlags) (string, error) {
|
|
||||||
parsed, err := url.Parse(u)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if f&FlagLowercaseHost == FlagLowercaseHost {
|
|
||||||
parsed.Host = strings.ToLower(parsed.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The idna package doesn't fully conform to RFC 5895
|
|
||||||
// (https://tools.ietf.org/html/rfc5895), so we do it here.
|
|
||||||
// Taken from Go 1.8 cycle source, courtesy of bradfitz.
|
|
||||||
// TODO: Remove when (if?) idna package conforms to RFC 5895.
|
|
||||||
parsed.Host = width.Fold.String(parsed.Host)
|
|
||||||
parsed.Host = norm.NFC.String(parsed.Host)
|
|
||||||
if parsed.Host, err = idna.ToASCII(parsed.Host); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return NormalizeURL(parsed, f), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalizeURL returns the normalized string.
|
|
||||||
// It takes a parsed URL object as input, as well as the normalization flags.
|
|
||||||
func NormalizeURL(u *url.URL, f NormalizationFlags) string {
|
|
||||||
for _, k := range flagsOrder {
|
|
||||||
if f&k == k {
|
|
||||||
flags[k](u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return urlesc.Escape(u)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lowercaseScheme(u *url.URL) {
|
|
||||||
if len(u.Scheme) > 0 {
|
|
||||||
u.Scheme = strings.ToLower(u.Scheme)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lowercaseHost(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
u.Host = strings.ToLower(u.Host)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDefaultPort(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
scheme := strings.ToLower(u.Scheme)
|
|
||||||
u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string {
|
|
||||||
if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeTrailingSlash(u *url.URL) {
|
|
||||||
if l := len(u.Path); l > 0 {
|
|
||||||
if strings.HasSuffix(u.Path, "/") {
|
|
||||||
u.Path = u.Path[:l-1]
|
|
||||||
}
|
|
||||||
} else if l = len(u.Host); l > 0 {
|
|
||||||
if strings.HasSuffix(u.Host, "/") {
|
|
||||||
u.Host = u.Host[:l-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addTrailingSlash(u *url.URL) {
|
|
||||||
if l := len(u.Path); l > 0 {
|
|
||||||
if !strings.HasSuffix(u.Path, "/") {
|
|
||||||
u.Path += "/"
|
|
||||||
}
|
|
||||||
} else if l = len(u.Host); l > 0 {
|
|
||||||
if !strings.HasSuffix(u.Host, "/") {
|
|
||||||
u.Host += "/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDotSegments(u *url.URL) {
|
|
||||||
if len(u.Path) > 0 {
|
|
||||||
var dotFree []string
|
|
||||||
var lastIsDot bool
|
|
||||||
|
|
||||||
sections := strings.Split(u.Path, "/")
|
|
||||||
for _, s := range sections {
|
|
||||||
if s == ".." {
|
|
||||||
if len(dotFree) > 0 {
|
|
||||||
dotFree = dotFree[:len(dotFree)-1]
|
|
||||||
}
|
|
||||||
} else if s != "." {
|
|
||||||
dotFree = append(dotFree, s)
|
|
||||||
}
|
|
||||||
lastIsDot = (s == "." || s == "..")
|
|
||||||
}
|
|
||||||
// Special case if host does not end with / and new path does not begin with /
|
|
||||||
u.Path = strings.Join(dotFree, "/")
|
|
||||||
if u.Host != "" && !strings.HasSuffix(u.Host, "/") && !strings.HasPrefix(u.Path, "/") {
|
|
||||||
u.Path = "/" + u.Path
|
|
||||||
}
|
|
||||||
// Special case if the last segment was a dot, make sure the path ends with a slash
|
|
||||||
if lastIsDot && !strings.HasSuffix(u.Path, "/") {
|
|
||||||
u.Path += "/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDirectoryIndex(u *url.URL) {
|
|
||||||
if len(u.Path) > 0 {
|
|
||||||
u.Path = rxDirIndex.ReplaceAllString(u.Path, "$1")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeFragment(u *url.URL) {
|
|
||||||
u.Fragment = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func forceHTTP(u *url.URL) {
|
|
||||||
if strings.ToLower(u.Scheme) == "https" {
|
|
||||||
u.Scheme = "http"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeDuplicateSlashes(u *url.URL) {
|
|
||||||
if len(u.Path) > 0 {
|
|
||||||
u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeWWW(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 && strings.HasPrefix(strings.ToLower(u.Host), "www.") {
|
|
||||||
u.Host = u.Host[4:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addWWW(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 && !strings.HasPrefix(strings.ToLower(u.Host), "www.") {
|
|
||||||
u.Host = "www." + u.Host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sortQuery(u *url.URL) {
|
|
||||||
q := u.Query()
|
|
||||||
|
|
||||||
if len(q) > 0 {
|
|
||||||
arKeys := make([]string, len(q))
|
|
||||||
i := 0
|
|
||||||
for k := range q {
|
|
||||||
arKeys[i] = k
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
sort.Strings(arKeys)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
for _, k := range arKeys {
|
|
||||||
sort.Strings(q[k])
|
|
||||||
for _, v := range q[k] {
|
|
||||||
if buf.Len() > 0 {
|
|
||||||
buf.WriteRune('&')
|
|
||||||
}
|
|
||||||
buf.WriteString(fmt.Sprintf("%s=%s", k, urlesc.QueryEscape(v)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebuild the raw query string
|
|
||||||
u.RawQuery = buf.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeDWORDHost(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
if matches := rxDWORDHost.FindStringSubmatch(u.Host); len(matches) > 2 {
|
|
||||||
var parts [4]int64
|
|
||||||
|
|
||||||
dword, _ := strconv.ParseInt(matches[1], 10, 0)
|
|
||||||
for i, shift := range []uint{24, 16, 8, 0} {
|
|
||||||
parts[i] = dword >> shift & 0xFF
|
|
||||||
}
|
|
||||||
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeOctalHost(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
if matches := rxOctalHost.FindStringSubmatch(u.Host); len(matches) > 5 {
|
|
||||||
var parts [4]int64
|
|
||||||
|
|
||||||
for i := 1; i <= 4; i++ {
|
|
||||||
parts[i-1], _ = strconv.ParseInt(matches[i], 8, 0)
|
|
||||||
}
|
|
||||||
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[5])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeHexHost(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
if matches := rxHexHost.FindStringSubmatch(u.Host); len(matches) > 2 {
|
|
||||||
// Conversion is safe because of regex validation
|
|
||||||
parsed, _ := strconv.ParseInt(matches[1], 16, 0)
|
|
||||||
// Set host as DWORD (base 10) encoded host
|
|
||||||
u.Host = fmt.Sprintf("%d%s", parsed, matches[2])
|
|
||||||
// The rest is the same as decoding a DWORD host
|
|
||||||
decodeDWORDHost(u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeUnncessaryHostDots(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
if matches := rxHostDots.FindStringSubmatch(u.Host); len(matches) > 1 {
|
|
||||||
// Trim the leading and trailing dots
|
|
||||||
u.Host = strings.Trim(matches[1], ".")
|
|
||||||
if len(matches) > 2 {
|
|
||||||
u.Host += matches[2]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeEmptyPortSeparator(u *url.URL) {
|
|
||||||
if len(u.Host) > 0 {
|
|
||||||
u.Host = rxEmptyPort.ReplaceAllString(u.Host, "")
|
|
||||||
}
|
|
||||||
}
|
|
15
vendor/github.com/PuerkitoBio/urlesc/.travis.yml
generated
vendored
15
vendor/github.com/PuerkitoBio/urlesc/.travis.yml
generated
vendored
@@ -1,15 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.4.x
|
|
||||||
- 1.5.x
|
|
||||||
- 1.6.x
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go build .
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v
|
|
16
vendor/github.com/PuerkitoBio/urlesc/README.md
generated
vendored
16
vendor/github.com/PuerkitoBio/urlesc/README.md
generated
vendored
@@ -1,16 +0,0 @@
|
|||||||
urlesc [](https://travis-ci.org/PuerkitoBio/urlesc) [](http://godoc.org/github.com/PuerkitoBio/urlesc)
|
|
||||||
======
|
|
||||||
|
|
||||||
Package urlesc implements query escaping as per RFC 3986.
|
|
||||||
|
|
||||||
It contains some parts of the net/url package, modified so as to allow
|
|
||||||
some reserved characters incorrectly escaped by net/url (see [issue 5684](https://github.com/golang/go/issues/5684)).
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
go get github.com/PuerkitoBio/urlesc
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Go license (BSD-3-Clause)
|
|
||||||
|
|
180
vendor/github.com/PuerkitoBio/urlesc/urlesc.go
generated
vendored
180
vendor/github.com/PuerkitoBio/urlesc/urlesc.go
generated
vendored
@@ -1,180 +0,0 @@
|
|||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package urlesc implements query escaping as per RFC 3986.
|
|
||||||
// It contains some parts of the net/url package, modified so as to allow
|
|
||||||
// some reserved characters incorrectly escaped by net/url.
|
|
||||||
// See https://github.com/golang/go/issues/5684
|
|
||||||
package urlesc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type encoding int
|
|
||||||
|
|
||||||
const (
|
|
||||||
encodePath encoding = 1 + iota
|
|
||||||
encodeUserPassword
|
|
||||||
encodeQueryComponent
|
|
||||||
encodeFragment
|
|
||||||
)
|
|
||||||
|
|
||||||
// Return true if the specified character should be escaped when
|
|
||||||
// appearing in a URL string, according to RFC 3986.
|
|
||||||
func shouldEscape(c byte, mode encoding) bool {
|
|
||||||
// §2.3 Unreserved characters (alphanum)
|
|
||||||
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch c {
|
|
||||||
case '-', '.', '_', '~': // §2.3 Unreserved characters (mark)
|
|
||||||
return false
|
|
||||||
|
|
||||||
// §2.2 Reserved characters (reserved)
|
|
||||||
case ':', '/', '?', '#', '[', ']', '@', // gen-delims
|
|
||||||
'!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // sub-delims
|
|
||||||
// Different sections of the URL allow a few of
|
|
||||||
// the reserved characters to appear unescaped.
|
|
||||||
switch mode {
|
|
||||||
case encodePath: // §3.3
|
|
||||||
// The RFC allows sub-delims and : @.
|
|
||||||
// '/', '[' and ']' can be used to assign meaning to individual path
|
|
||||||
// segments. This package only manipulates the path as a whole,
|
|
||||||
// so we allow those as well. That leaves only ? and # to escape.
|
|
||||||
return c == '?' || c == '#'
|
|
||||||
|
|
||||||
case encodeUserPassword: // §3.2.1
|
|
||||||
// The RFC allows : and sub-delims in
|
|
||||||
// userinfo. The parsing of userinfo treats ':' as special so we must escape
|
|
||||||
// all the gen-delims.
|
|
||||||
return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'
|
|
||||||
|
|
||||||
case encodeQueryComponent: // §3.4
|
|
||||||
// The RFC allows / and ?.
|
|
||||||
return c != '/' && c != '?'
|
|
||||||
|
|
||||||
case encodeFragment: // §4.1
|
|
||||||
// The RFC text is silent but the grammar allows
|
|
||||||
// everything, so escape nothing but #
|
|
||||||
return c == '#'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything else must be escaped.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryEscape escapes the string so it can be safely placed
|
|
||||||
// inside a URL query.
|
|
||||||
func QueryEscape(s string) string {
|
|
||||||
return escape(s, encodeQueryComponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func escape(s string, mode encoding) string {
|
|
||||||
spaceCount, hexCount := 0, 0
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
c := s[i]
|
|
||||||
if shouldEscape(c, mode) {
|
|
||||||
if c == ' ' && mode == encodeQueryComponent {
|
|
||||||
spaceCount++
|
|
||||||
} else {
|
|
||||||
hexCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if spaceCount == 0 && hexCount == 0 {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
t := make([]byte, len(s)+2*hexCount)
|
|
||||||
j := 0
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
switch c := s[i]; {
|
|
||||||
case c == ' ' && mode == encodeQueryComponent:
|
|
||||||
t[j] = '+'
|
|
||||||
j++
|
|
||||||
case shouldEscape(c, mode):
|
|
||||||
t[j] = '%'
|
|
||||||
t[j+1] = "0123456789ABCDEF"[c>>4]
|
|
||||||
t[j+2] = "0123456789ABCDEF"[c&15]
|
|
||||||
j += 3
|
|
||||||
default:
|
|
||||||
t[j] = s[i]
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
var uiReplacer = strings.NewReplacer(
|
|
||||||
"%21", "!",
|
|
||||||
"%27", "'",
|
|
||||||
"%28", "(",
|
|
||||||
"%29", ")",
|
|
||||||
"%2A", "*",
|
|
||||||
)
|
|
||||||
|
|
||||||
// unescapeUserinfo unescapes some characters that need not to be escaped as per RFC3986.
|
|
||||||
func unescapeUserinfo(s string) string {
|
|
||||||
return uiReplacer.Replace(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escape reassembles the URL into a valid URL string.
|
|
||||||
// The general form of the result is one of:
|
|
||||||
//
|
|
||||||
// scheme:opaque
|
|
||||||
// scheme://userinfo@host/path?query#fragment
|
|
||||||
//
|
|
||||||
// If u.Opaque is non-empty, String uses the first form;
|
|
||||||
// otherwise it uses the second form.
|
|
||||||
//
|
|
||||||
// In the second form, the following rules apply:
|
|
||||||
// - if u.Scheme is empty, scheme: is omitted.
|
|
||||||
// - if u.User is nil, userinfo@ is omitted.
|
|
||||||
// - if u.Host is empty, host/ is omitted.
|
|
||||||
// - if u.Scheme and u.Host are empty and u.User is nil,
|
|
||||||
// the entire scheme://userinfo@host/ is omitted.
|
|
||||||
// - if u.Host is non-empty and u.Path begins with a /,
|
|
||||||
// the form host/path does not add its own /.
|
|
||||||
// - if u.RawQuery is empty, ?query is omitted.
|
|
||||||
// - if u.Fragment is empty, #fragment is omitted.
|
|
||||||
func Escape(u *url.URL) string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if u.Scheme != "" {
|
|
||||||
buf.WriteString(u.Scheme)
|
|
||||||
buf.WriteByte(':')
|
|
||||||
}
|
|
||||||
if u.Opaque != "" {
|
|
||||||
buf.WriteString(u.Opaque)
|
|
||||||
} else {
|
|
||||||
if u.Scheme != "" || u.Host != "" || u.User != nil {
|
|
||||||
buf.WriteString("//")
|
|
||||||
if ui := u.User; ui != nil {
|
|
||||||
buf.WriteString(unescapeUserinfo(ui.String()))
|
|
||||||
buf.WriteByte('@')
|
|
||||||
}
|
|
||||||
if h := u.Host; h != "" {
|
|
||||||
buf.WriteString(h)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if u.Path != "" && u.Path[0] != '/' && u.Host != "" {
|
|
||||||
buf.WriteByte('/')
|
|
||||||
}
|
|
||||||
buf.WriteString(escape(u.Path, encodePath))
|
|
||||||
}
|
|
||||||
if u.RawQuery != "" {
|
|
||||||
buf.WriteByte('?')
|
|
||||||
buf.WriteString(u.RawQuery)
|
|
||||||
}
|
|
||||||
if u.Fragment != "" {
|
|
||||||
buf.WriteByte('#')
|
|
||||||
buf.WriteString(escape(u.Fragment, encodeFragment))
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
185
vendor/github.com/ant31/crd-validation/pkg/cli-utils.go
generated
vendored
185
vendor/github.com/ant31/crd-validation/pkg/cli-utils.go
generated
vendored
@@ -1,185 +0,0 @@
|
|||||||
// Copyright 2018
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package crdvalidation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
|
||||||
extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config stores the user configuration input
|
|
||||||
type Config struct {
|
|
||||||
SpecDefinitionName string
|
|
||||||
EnableValidation bool
|
|
||||||
OutputFormat string
|
|
||||||
Labels Labels
|
|
||||||
Annotations Labels
|
|
||||||
ResourceScope string
|
|
||||||
Group string
|
|
||||||
Kind string
|
|
||||||
Version string
|
|
||||||
Plural string
|
|
||||||
SpecReplicasPath string
|
|
||||||
StatusReplicasPath string
|
|
||||||
LabelSelectorPath string
|
|
||||||
Categories []string
|
|
||||||
ShortNames []string
|
|
||||||
GetOpenAPIDefinitions GetAPIDefinitions
|
|
||||||
}
|
|
||||||
|
|
||||||
type Labels struct {
|
|
||||||
LabelsString string
|
|
||||||
LabelsMap map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement the flag.Value interface
|
|
||||||
func (labels *Labels) String() string {
|
|
||||||
return labels.LabelsString
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge labels create a new map with labels merged.
|
|
||||||
func (labels *Labels) Merge(otherLabels map[string]string) map[string]string {
|
|
||||||
mergedLabels := map[string]string{}
|
|
||||||
|
|
||||||
for key, value := range otherLabels {
|
|
||||||
mergedLabels[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range labels.LabelsMap {
|
|
||||||
mergedLabels[key] = value
|
|
||||||
}
|
|
||||||
return mergedLabels
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement the flag.Set interface
|
|
||||||
func (labels *Labels) Set(value string) error {
|
|
||||||
m := map[string]string{}
|
|
||||||
if value != "" {
|
|
||||||
splited := strings.Split(value, ",")
|
|
||||||
for _, pair := range splited {
|
|
||||||
sp := strings.Split(pair, "=")
|
|
||||||
m[sp[0]] = sp[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(*labels).LabelsMap = m
|
|
||||||
(*labels).LabelsString = value
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCustomResourceDefinition(config Config) *extensionsobj.CustomResourceDefinition {
|
|
||||||
|
|
||||||
crd := &extensionsobj.CustomResourceDefinition{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: config.Plural + "." + config.Group,
|
|
||||||
Labels: config.Labels.LabelsMap,
|
|
||||||
Annotations: config.Annotations.LabelsMap,
|
|
||||||
},
|
|
||||||
TypeMeta: CustomResourceDefinitionTypeMeta,
|
|
||||||
Spec: extensionsobj.CustomResourceDefinitionSpec{
|
|
||||||
Group: config.Group,
|
|
||||||
Version: config.Version,
|
|
||||||
Scope: extensionsobj.ResourceScope(config.ResourceScope),
|
|
||||||
Names: extensionsobj.CustomResourceDefinitionNames{
|
|
||||||
Plural: config.Plural,
|
|
||||||
Kind: config.Kind,
|
|
||||||
Categories: config.Categories,
|
|
||||||
ShortNames: config.ShortNames,
|
|
||||||
},
|
|
||||||
Subresources: &extensionsobj.CustomResourceSubresources{
|
|
||||||
Status: &extensionsobj.CustomResourceSubresourceStatus {
|
|
||||||
},
|
|
||||||
Scale: &extensionsobj.CustomResourceSubresourceScale {
|
|
||||||
SpecReplicasPath: config.SpecReplicasPath,
|
|
||||||
StatusReplicasPath: config.StatusReplicasPath,
|
|
||||||
LabelSelectorPath: &config.LabelSelectorPath,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.SpecDefinitionName != "" && config.EnableValidation == true {
|
|
||||||
crd.Spec.Validation = GetCustomResourceValidation(config.SpecDefinitionName, config.GetOpenAPIDefinitions)
|
|
||||||
}
|
|
||||||
|
|
||||||
return crd
|
|
||||||
}
|
|
||||||
|
|
||||||
func MarshallCrd(crd *extensionsobj.CustomResourceDefinition, outputFormat string) error {
|
|
||||||
jsonBytes, err := json.Marshal(crd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var r unstructured.Unstructured
|
|
||||||
if err := json.Unmarshal(jsonBytes, &r.Object); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
unstructured.RemoveNestedField(r.Object, "status")
|
|
||||||
|
|
||||||
jsonBytes, err = json.MarshalIndent(r.Object, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if outputFormat == "json" {
|
|
||||||
_, err = os.Stdout.Write(jsonBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
yamlBytes, err := yaml.JSONToYAML(jsonBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = os.Stdout.Write([]byte("---\n"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = os.Stdout.Write(yamlBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitFlags prepares command line flags parser
|
|
||||||
func InitFlags(cfg *Config, flagset *flag.FlagSet) *flag.FlagSet {
|
|
||||||
flagset.Var(&cfg.Labels, "labels", "Labels")
|
|
||||||
flagset.Var(&cfg.Annotations, "annotations", "Annotations")
|
|
||||||
flagset.BoolVar(&cfg.EnableValidation, "with-validation", true, "Add CRD validation field, default: true")
|
|
||||||
flagset.StringVar(&cfg.Group, "apigroup", "custom.example.com", "CRD api group")
|
|
||||||
flagset.StringVar(&cfg.SpecDefinitionName, "spec-name", "", "CRD spec definition name")
|
|
||||||
flagset.StringVar(&cfg.OutputFormat, "output", "yaml", "output format: json|yaml")
|
|
||||||
flagset.StringVar(&cfg.Kind, "kind", "", "CRD Kind")
|
|
||||||
flagset.StringVar(&cfg.ResourceScope, "scope", string(extensionsobj.NamespaceScoped), "CRD scope: 'Namespaced' | 'Cluster'. Default: Namespaced")
|
|
||||||
flagset.StringVar(&cfg.Version, "version", "v1", "CRD version, default: 'v1'")
|
|
||||||
flagset.StringVar(&cfg.Plural, "plural", "", "CRD plural name")
|
|
||||||
flagset.StringVar(&cfg.SpecReplicasPath, "spec-replicas-path", ".spec.replicas", "CRD spec replicas path")
|
|
||||||
flagset.StringVar(&cfg.StatusReplicasPath, "status-replicas-path", ".status.replicas", "CRD status replicas path")
|
|
||||||
flagset.StringVar(&cfg.LabelSelectorPath, "label-selector-path", ".status.labelSelector", "CRD label selector path")
|
|
||||||
return flagset
|
|
||||||
}
|
|
126
vendor/github.com/ant31/crd-validation/pkg/convert_types.go
generated
vendored
126
vendor/github.com/ant31/crd-validation/pkg/convert_types.go
generated
vendored
@@ -1,126 +0,0 @@
|
|||||||
package crdvalidation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
spec "github.com/go-openapi/spec"
|
|
||||||
extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
|
||||||
common "k8s.io/kube-openapi/pkg/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SchemaPropsToJSONPropsArray converts []Schema to []JSONSchemaProps
|
|
||||||
func SchemaPropsToJSONPropsArray(schemas []spec.Schema, openapiSpec map[string]common.OpenAPIDefinition, nested bool) []extensionsobj.JSONSchemaProps {
|
|
||||||
var s []extensionsobj.JSONSchemaProps
|
|
||||||
for _, schema := range schemas {
|
|
||||||
s = append(s, *SchemaPropsToJSONProps(&schema, openapiSpec, nested))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringOrArrayToString converts StringOrArray to string
|
|
||||||
func StringOrArrayToString(strOrArray spec.StringOrArray) string {
|
|
||||||
if len(strOrArray) > 0 {
|
|
||||||
return strOrArray[0]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnumJSON converts []interface{} to []JSON
|
|
||||||
func EnumJSON(enum []interface{}) []extensionsobj.JSON {
|
|
||||||
var s []extensionsobj.JSON
|
|
||||||
for _, elt := range enum {
|
|
||||||
s = append(s, extensionsobj.JSON{
|
|
||||||
Raw: []byte(fmt.Sprintf("%v", elt)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// SchemaOrArrayToJSONItems converts *SchemaOrArray to *JSONSchemaPropsOrArray
|
|
||||||
func SchemaOrArrayToJSONItems(schemaOrArray *spec.SchemaOrArray, openapiSpec map[string]common.OpenAPIDefinition, nested bool) *extensionsobj.JSONSchemaPropsOrArray {
|
|
||||||
var array *extensionsobj.JSONSchemaPropsOrArray
|
|
||||||
if schemaOrArray == nil {
|
|
||||||
return array
|
|
||||||
}
|
|
||||||
return &extensionsobj.JSONSchemaPropsOrArray{
|
|
||||||
Schema: SchemaPropsToJSONProps(schemaOrArray.Schema, openapiSpec, nested),
|
|
||||||
JSONSchemas: SchemaPropsToJSONPropsArray(schemaOrArray.Schemas, openapiSpec, nested),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SchemaOrBoolToJSONProps converts *SchemaOrBool to *JSONSchemaPropsOrBool
|
|
||||||
func SchemaOrBoolToJSONProps(schemaOrBool *spec.SchemaOrBool, openapiSpec map[string]common.OpenAPIDefinition, nested bool) *extensionsobj.JSONSchemaPropsOrBool {
|
|
||||||
var s *extensionsobj.JSONSchemaPropsOrBool
|
|
||||||
if schemaOrBool == nil {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return &extensionsobj.JSONSchemaPropsOrBool{
|
|
||||||
Schema: SchemaPropsToJSONProps(schemaOrBool.Schema, openapiSpec, nested),
|
|
||||||
Allows: schemaOrBool.Allows,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SchemPropsMapToJSONMap converts map[string]Schema to map[string]JSONSchemaProps
|
|
||||||
func SchemPropsMapToJSONMap(schemaMap map[string]spec.Schema, openapiSpec map[string]common.OpenAPIDefinition, nested bool) map[string]extensionsobj.JSONSchemaProps {
|
|
||||||
var m map[string]extensionsobj.JSONSchemaProps
|
|
||||||
m = make(map[string]extensionsobj.JSONSchemaProps)
|
|
||||||
for key, schema := range schemaMap {
|
|
||||||
m[key] = *SchemaPropsToJSONProps(&schema, openapiSpec, nested)
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// SchemaPropsToJSONProps converts a SchemaProps to a JSONProps
|
|
||||||
func SchemaPropsToJSONProps(schema *spec.Schema, openapiSpec map[string]common.OpenAPIDefinition, nested bool) *extensionsobj.JSONSchemaProps {
|
|
||||||
var props *extensionsobj.JSONSchemaProps
|
|
||||||
if schema == nil {
|
|
||||||
return props
|
|
||||||
}
|
|
||||||
schemaProps := &schema.SchemaProps
|
|
||||||
|
|
||||||
var ref *string
|
|
||||||
if schemaProps.Ref.String() != "" {
|
|
||||||
if nested {
|
|
||||||
propref := openapiSpec[schemaProps.Ref.String()].Schema
|
|
||||||
// If nested just return a pointer to the reference
|
|
||||||
return SchemaPropsToJSONProps(&propref, openapiSpec, nested)
|
|
||||||
}
|
|
||||||
ref = new(string)
|
|
||||||
*ref = schemaProps.Ref.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
props = &extensionsobj.JSONSchemaProps{
|
|
||||||
Ref: ref,
|
|
||||||
ID: schemaProps.ID,
|
|
||||||
Schema: extensionsobj.JSONSchemaURL(string(schema.Schema)),
|
|
||||||
Description: schemaProps.Description,
|
|
||||||
Type: StringOrArrayToString(schemaProps.Type),
|
|
||||||
Format: schemaProps.Format,
|
|
||||||
Title: schemaProps.Title,
|
|
||||||
Maximum: schemaProps.Maximum,
|
|
||||||
ExclusiveMaximum: schemaProps.ExclusiveMaximum,
|
|
||||||
Minimum: schemaProps.Minimum,
|
|
||||||
ExclusiveMinimum: schemaProps.ExclusiveMinimum,
|
|
||||||
MaxLength: schemaProps.MaxLength,
|
|
||||||
MinLength: schemaProps.MinLength,
|
|
||||||
Pattern: schemaProps.Pattern,
|
|
||||||
MaxItems: schemaProps.MaxItems,
|
|
||||||
MinItems: schemaProps.MinItems,
|
|
||||||
UniqueItems: schemaProps.UniqueItems,
|
|
||||||
MultipleOf: schemaProps.MultipleOf,
|
|
||||||
Enum: EnumJSON(schemaProps.Enum),
|
|
||||||
MaxProperties: schemaProps.MaxProperties,
|
|
||||||
MinProperties: schemaProps.MinProperties,
|
|
||||||
Required: schemaProps.Required,
|
|
||||||
Items: SchemaOrArrayToJSONItems(schemaProps.Items, openapiSpec, nested),
|
|
||||||
AllOf: SchemaPropsToJSONPropsArray(schemaProps.AllOf, openapiSpec, nested),
|
|
||||||
OneOf: SchemaPropsToJSONPropsArray(schemaProps.OneOf, openapiSpec, nested),
|
|
||||||
AnyOf: SchemaPropsToJSONPropsArray(schemaProps.AnyOf, openapiSpec, nested),
|
|
||||||
Not: SchemaPropsToJSONProps(schemaProps.Not, openapiSpec, nested),
|
|
||||||
Properties: SchemPropsMapToJSONMap(schemaProps.Properties, openapiSpec, nested),
|
|
||||||
// @TODO(01-25-2018) Field not accepted by the current CRD Validation Spec
|
|
||||||
// AdditionalProperties: SchemaOrBoolToJSONProps(schemaProps.AdditionalProperties, openapiSpec, nested),
|
|
||||||
PatternProperties: SchemPropsMapToJSONMap(schemaProps.PatternProperties, openapiSpec, nested),
|
|
||||||
AdditionalItems: SchemaOrBoolToJSONProps(schemaProps.AdditionalItems, openapiSpec, nested),
|
|
||||||
}
|
|
||||||
return props
|
|
||||||
}
|
|
72
vendor/github.com/ant31/crd-validation/pkg/crdvalidation.go
generated
vendored
72
vendor/github.com/ant31/crd-validation/pkg/crdvalidation.go
generated
vendored
@@ -1,72 +0,0 @@
|
|||||||
package crdvalidation
|
|
||||||
|
|
||||||
import (
|
|
||||||
spec "github.com/go-openapi/spec"
|
|
||||||
extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
common "k8s.io/kube-openapi/pkg/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CustomResourceDefinitionTypeMeta set the default kind/apiversion of CRD
|
|
||||||
var CustomResourceDefinitionTypeMeta = metav1.TypeMeta{
|
|
||||||
Kind: "CustomResourceDefinition",
|
|
||||||
APIVersion: "apiextensions.k8s.io/v1beta1",
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenAPIRefCallBack returns a jsonref using the input string without modification
|
|
||||||
func OpenAPIRefCallBack(name string) spec.Ref {
|
|
||||||
return spec.MustCreateRef(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAPIDefinition is a function returning a map with all Definition
|
|
||||||
type GetAPIDefinitions func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition
|
|
||||||
|
|
||||||
// GetCustomResourceValidations returns a CRD validation spec map. It took the openapi generated definition from kube-openapi as argument
|
|
||||||
func GetCustomResourceValidations(fn GetAPIDefinitions) map[string]*extensionsobj.CustomResourceValidation {
|
|
||||||
openapiSpec := fn(OpenAPIRefCallBack)
|
|
||||||
var definitions map[string]*extensionsobj.CustomResourceValidation
|
|
||||||
definitions = make(map[string]*extensionsobj.CustomResourceValidation)
|
|
||||||
for key, definition := range openapiSpec {
|
|
||||||
schema := definition.Schema
|
|
||||||
definitions[key] = &extensionsobj.CustomResourceValidation{
|
|
||||||
OpenAPIV3Schema: SchemaPropsToJSONProps(&schema, openapiSpec, true),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return definitions
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCustomResourceValidation returns the validation definition for a CRD name
|
|
||||||
func GetCustomResourceValidation(name string, fn func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition) *extensionsobj.CustomResourceValidation {
|
|
||||||
openapiSpec := fn(OpenAPIRefCallBack)
|
|
||||||
fixKnownTypes(openapiSpec)
|
|
||||||
schema := openapiSpec[name].Schema
|
|
||||||
crv := &extensionsobj.CustomResourceValidation{
|
|
||||||
OpenAPIV3Schema: SchemaPropsToJSONProps(&schema, openapiSpec, true),
|
|
||||||
}
|
|
||||||
crv.OpenAPIV3Schema.Description = ""
|
|
||||||
crv.OpenAPIV3Schema.Required = nil
|
|
||||||
return crv
|
|
||||||
}
|
|
||||||
|
|
||||||
// ref: https://github.com/kubernetes/kubernetes/issues/62329
|
|
||||||
func fixKnownTypes(openapiSpec map[string]common.OpenAPIDefinition) {
|
|
||||||
openapiSpec["k8s.io/apimachinery/pkg/util/intstr.IntOrString"] = common.OpenAPIDefinition{
|
|
||||||
Schema: spec.Schema{
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
AnyOf: []spec.Schema{
|
|
||||||
{
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
Type: []string{"string"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SchemaProps: spec.SchemaProps{
|
|
||||||
Type: []string{"integer"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
8
vendor/github.com/cespare/xxhash/v2/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/cespare/xxhash/v2/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- "1.x"
|
||||||
|
- master
|
||||||
|
env:
|
||||||
|
- TAGS=""
|
||||||
|
- TAGS="-tags purego"
|
||||||
|
script: go test $TAGS -v ./...
|
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2012,2013 Ernest Micklei
|
Copyright (c) 2016 Caleb Spare
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
67
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
Normal file
67
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# xxhash
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/cespare/xxhash)
|
||||||
|
[](https://travis-ci.org/cespare/xxhash)
|
||||||
|
|
||||||
|
xxhash is a Go implementation of the 64-bit
|
||||||
|
[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a
|
||||||
|
high-quality hashing algorithm that is much faster than anything in the Go
|
||||||
|
standard library.
|
||||||
|
|
||||||
|
This package provides a straightforward API:
|
||||||
|
|
||||||
|
```
|
||||||
|
func Sum64(b []byte) uint64
|
||||||
|
func Sum64String(s string) uint64
|
||||||
|
type Digest struct{ ... }
|
||||||
|
func New() *Digest
|
||||||
|
```
|
||||||
|
|
||||||
|
The `Digest` type implements hash.Hash64. Its key methods are:
|
||||||
|
|
||||||
|
```
|
||||||
|
func (*Digest) Write([]byte) (int, error)
|
||||||
|
func (*Digest) WriteString(string) (int, error)
|
||||||
|
func (*Digest) Sum64() uint64
|
||||||
|
```
|
||||||
|
|
||||||
|
This implementation provides a fast pure-Go implementation and an even faster
|
||||||
|
assembly implementation for amd64.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
This package is in a module and the latest code is in version 2 of the module.
|
||||||
|
You need a version of Go with at least "minimal module compatibility" to use
|
||||||
|
github.com/cespare/xxhash/v2:
|
||||||
|
|
||||||
|
* 1.9.7+ for Go 1.9
|
||||||
|
* 1.10.3+ for Go 1.10
|
||||||
|
* Go 1.11 or later
|
||||||
|
|
||||||
|
I recommend using the latest release of Go.
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
Here are some quick benchmarks comparing the pure-Go and assembly
|
||||||
|
implementations of Sum64.
|
||||||
|
|
||||||
|
| input size | purego | asm |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 5 B | 979.66 MB/s | 1291.17 MB/s |
|
||||||
|
| 100 B | 7475.26 MB/s | 7973.40 MB/s |
|
||||||
|
| 4 KB | 17573.46 MB/s | 17602.65 MB/s |
|
||||||
|
| 10 MB | 17131.46 MB/s | 17142.16 MB/s |
|
||||||
|
|
||||||
|
These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using
|
||||||
|
the following commands under Go 1.11.2:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes'
|
||||||
|
$ go test -benchtime 10s -bench '/xxhash,direct,bytes'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Projects using this package
|
||||||
|
|
||||||
|
- [InfluxDB](https://github.com/influxdata/influxdb)
|
||||||
|
- [Prometheus](https://github.com/prometheus/prometheus)
|
||||||
|
- [FreeCache](https://github.com/coocood/freecache)
|
3
vendor/github.com/cespare/xxhash/v2/go.mod
generated
vendored
Normal file
3
vendor/github.com/cespare/xxhash/v2/go.mod
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module github.com/cespare/xxhash/v2
|
||||||
|
|
||||||
|
go 1.11
|
0
vendor/github.com/cespare/xxhash/v2/go.sum
generated
vendored
Normal file
0
vendor/github.com/cespare/xxhash/v2/go.sum
generated
vendored
Normal file
236
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
Normal file
236
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described
|
||||||
|
// at http://cyan4973.github.io/xxHash/.
|
||||||
|
package xxhash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
prime1 uint64 = 11400714785074694791
|
||||||
|
prime2 uint64 = 14029467366897019727
|
||||||
|
prime3 uint64 = 1609587929392839161
|
||||||
|
prime4 uint64 = 9650029242287828579
|
||||||
|
prime5 uint64 = 2870177450012600261
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where
|
||||||
|
// possible in the Go code is worth a small (but measurable) performance boost
|
||||||
|
// by avoiding some MOVQs. Vars are needed for the asm and also are useful for
|
||||||
|
// convenience in the Go code in a few places where we need to intentionally
|
||||||
|
// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the
|
||||||
|
// result overflows a uint64).
|
||||||
|
var (
|
||||||
|
prime1v = prime1
|
||||||
|
prime2v = prime2
|
||||||
|
prime3v = prime3
|
||||||
|
prime4v = prime4
|
||||||
|
prime5v = prime5
|
||||||
|
)
|
||||||
|
|
||||||
|
// Digest implements hash.Hash64.
|
||||||
|
type Digest struct {
|
||||||
|
v1 uint64
|
||||||
|
v2 uint64
|
||||||
|
v3 uint64
|
||||||
|
v4 uint64
|
||||||
|
total uint64
|
||||||
|
mem [32]byte
|
||||||
|
n int // how much of mem is used
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Digest that computes the 64-bit xxHash algorithm.
|
||||||
|
func New() *Digest {
|
||||||
|
var d Digest
|
||||||
|
d.Reset()
|
||||||
|
return &d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset clears the Digest's state so that it can be reused.
|
||||||
|
func (d *Digest) Reset() {
|
||||||
|
d.v1 = prime1v + prime2
|
||||||
|
d.v2 = prime2
|
||||||
|
d.v3 = 0
|
||||||
|
d.v4 = -prime1v
|
||||||
|
d.total = 0
|
||||||
|
d.n = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size always returns 8 bytes.
|
||||||
|
func (d *Digest) Size() int { return 8 }
|
||||||
|
|
||||||
|
// BlockSize always returns 32 bytes.
|
||||||
|
func (d *Digest) BlockSize() int { return 32 }
|
||||||
|
|
||||||
|
// Write adds more data to d. It always returns len(b), nil.
|
||||||
|
func (d *Digest) Write(b []byte) (n int, err error) {
|
||||||
|
n = len(b)
|
||||||
|
d.total += uint64(n)
|
||||||
|
|
||||||
|
if d.n+n < 32 {
|
||||||
|
// This new data doesn't even fill the current block.
|
||||||
|
copy(d.mem[d.n:], b)
|
||||||
|
d.n += n
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.n > 0 {
|
||||||
|
// Finish off the partial block.
|
||||||
|
copy(d.mem[d.n:], b)
|
||||||
|
d.v1 = round(d.v1, u64(d.mem[0:8]))
|
||||||
|
d.v2 = round(d.v2, u64(d.mem[8:16]))
|
||||||
|
d.v3 = round(d.v3, u64(d.mem[16:24]))
|
||||||
|
d.v4 = round(d.v4, u64(d.mem[24:32]))
|
||||||
|
b = b[32-d.n:]
|
||||||
|
d.n = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) >= 32 {
|
||||||
|
// One or more full blocks left.
|
||||||
|
nw := writeBlocks(d, b)
|
||||||
|
b = b[nw:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store any remaining partial block.
|
||||||
|
copy(d.mem[:], b)
|
||||||
|
d.n = len(b)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum appends the current hash to b and returns the resulting slice.
|
||||||
|
func (d *Digest) Sum(b []byte) []byte {
|
||||||
|
s := d.Sum64()
|
||||||
|
return append(
|
||||||
|
b,
|
||||||
|
byte(s>>56),
|
||||||
|
byte(s>>48),
|
||||||
|
byte(s>>40),
|
||||||
|
byte(s>>32),
|
||||||
|
byte(s>>24),
|
||||||
|
byte(s>>16),
|
||||||
|
byte(s>>8),
|
||||||
|
byte(s),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum64 returns the current hash.
|
||||||
|
func (d *Digest) Sum64() uint64 {
|
||||||
|
var h uint64
|
||||||
|
|
||||||
|
if d.total >= 32 {
|
||||||
|
v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
|
||||||
|
h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
|
||||||
|
h = mergeRound(h, v1)
|
||||||
|
h = mergeRound(h, v2)
|
||||||
|
h = mergeRound(h, v3)
|
||||||
|
h = mergeRound(h, v4)
|
||||||
|
} else {
|
||||||
|
h = d.v3 + prime5
|
||||||
|
}
|
||||||
|
|
||||||
|
h += d.total
|
||||||
|
|
||||||
|
i, end := 0, d.n
|
||||||
|
for ; i+8 <= end; i += 8 {
|
||||||
|
k1 := round(0, u64(d.mem[i:i+8]))
|
||||||
|
h ^= k1
|
||||||
|
h = rol27(h)*prime1 + prime4
|
||||||
|
}
|
||||||
|
if i+4 <= end {
|
||||||
|
h ^= uint64(u32(d.mem[i:i+4])) * prime1
|
||||||
|
h = rol23(h)*prime2 + prime3
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
for i < end {
|
||||||
|
h ^= uint64(d.mem[i]) * prime5
|
||||||
|
h = rol11(h) * prime1
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
h ^= h >> 33
|
||||||
|
h *= prime2
|
||||||
|
h ^= h >> 29
|
||||||
|
h *= prime3
|
||||||
|
h ^= h >> 32
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
magic = "xxh\x06"
|
||||||
|
marshaledSize = len(magic) + 8*5 + 32
|
||||||
|
)
|
||||||
|
|
||||||
|
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
||||||
|
func (d *Digest) MarshalBinary() ([]byte, error) {
|
||||||
|
b := make([]byte, 0, marshaledSize)
|
||||||
|
b = append(b, magic...)
|
||||||
|
b = appendUint64(b, d.v1)
|
||||||
|
b = appendUint64(b, d.v2)
|
||||||
|
b = appendUint64(b, d.v3)
|
||||||
|
b = appendUint64(b, d.v4)
|
||||||
|
b = appendUint64(b, d.total)
|
||||||
|
b = append(b, d.mem[:d.n]...)
|
||||||
|
b = b[:len(b)+len(d.mem)-d.n]
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
|
||||||
|
func (d *Digest) UnmarshalBinary(b []byte) error {
|
||||||
|
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
|
||||||
|
return errors.New("xxhash: invalid hash state identifier")
|
||||||
|
}
|
||||||
|
if len(b) != marshaledSize {
|
||||||
|
return errors.New("xxhash: invalid hash state size")
|
||||||
|
}
|
||||||
|
b = b[len(magic):]
|
||||||
|
b, d.v1 = consumeUint64(b)
|
||||||
|
b, d.v2 = consumeUint64(b)
|
||||||
|
b, d.v3 = consumeUint64(b)
|
||||||
|
b, d.v4 = consumeUint64(b)
|
||||||
|
b, d.total = consumeUint64(b)
|
||||||
|
copy(d.mem[:], b)
|
||||||
|
b = b[len(d.mem):]
|
||||||
|
d.n = int(d.total % uint64(len(d.mem)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendUint64(b []byte, x uint64) []byte {
|
||||||
|
var a [8]byte
|
||||||
|
binary.LittleEndian.PutUint64(a[:], x)
|
||||||
|
return append(b, a[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func consumeUint64(b []byte) ([]byte, uint64) {
|
||||||
|
x := u64(b)
|
||||||
|
return b[8:], x
|
||||||
|
}
|
||||||
|
|
||||||
|
func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) }
|
||||||
|
func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) }
|
||||||
|
|
||||||
|
func round(acc, input uint64) uint64 {
|
||||||
|
acc += input * prime2
|
||||||
|
acc = rol31(acc)
|
||||||
|
acc *= prime1
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeRound(acc, val uint64) uint64 {
|
||||||
|
val = round(0, val)
|
||||||
|
acc ^= val
|
||||||
|
acc = acc*prime1 + prime4
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) }
|
||||||
|
func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) }
|
||||||
|
func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) }
|
||||||
|
func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) }
|
||||||
|
func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) }
|
||||||
|
func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) }
|
||||||
|
func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) }
|
||||||
|
func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) }
|
13
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go
generated
vendored
Normal file
13
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !purego
|
||||||
|
|
||||||
|
package xxhash
|
||||||
|
|
||||||
|
// Sum64 computes the 64-bit xxHash digest of b.
|
||||||
|
//
|
||||||
|
//go:noescape
|
||||||
|
func Sum64(b []byte) uint64
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func writeBlocks(d *Digest, b []byte) int
|
215
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s
generated
vendored
Normal file
215
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// Register allocation:
|
||||||
|
// AX h
|
||||||
|
// CX pointer to advance through b
|
||||||
|
// DX n
|
||||||
|
// BX loop end
|
||||||
|
// R8 v1, k1
|
||||||
|
// R9 v2
|
||||||
|
// R10 v3
|
||||||
|
// R11 v4
|
||||||
|
// R12 tmp
|
||||||
|
// R13 prime1v
|
||||||
|
// R14 prime2v
|
||||||
|
// R15 prime4v
|
||||||
|
|
||||||
|
// round reads from and advances the buffer pointer in CX.
|
||||||
|
// It assumes that R13 has prime1v and R14 has prime2v.
|
||||||
|
#define round(r) \
|
||||||
|
MOVQ (CX), R12 \
|
||||||
|
ADDQ $8, CX \
|
||||||
|
IMULQ R14, R12 \
|
||||||
|
ADDQ R12, r \
|
||||||
|
ROLQ $31, r \
|
||||||
|
IMULQ R13, r
|
||||||
|
|
||||||
|
// mergeRound applies a merge round on the two registers acc and val.
|
||||||
|
// It assumes that R13 has prime1v, R14 has prime2v, and R15 has prime4v.
|
||||||
|
#define mergeRound(acc, val) \
|
||||||
|
IMULQ R14, val \
|
||||||
|
ROLQ $31, val \
|
||||||
|
IMULQ R13, val \
|
||||||
|
XORQ val, acc \
|
||||||
|
IMULQ R13, acc \
|
||||||
|
ADDQ R15, acc
|
||||||
|
|
||||||
|
// func Sum64(b []byte) uint64
|
||||||
|
TEXT ·Sum64(SB), NOSPLIT, $0-32
|
||||||
|
// Load fixed primes.
|
||||||
|
MOVQ ·prime1v(SB), R13
|
||||||
|
MOVQ ·prime2v(SB), R14
|
||||||
|
MOVQ ·prime4v(SB), R15
|
||||||
|
|
||||||
|
// Load slice.
|
||||||
|
MOVQ b_base+0(FP), CX
|
||||||
|
MOVQ b_len+8(FP), DX
|
||||||
|
LEAQ (CX)(DX*1), BX
|
||||||
|
|
||||||
|
// The first loop limit will be len(b)-32.
|
||||||
|
SUBQ $32, BX
|
||||||
|
|
||||||
|
// Check whether we have at least one block.
|
||||||
|
CMPQ DX, $32
|
||||||
|
JLT noBlocks
|
||||||
|
|
||||||
|
// Set up initial state (v1, v2, v3, v4).
|
||||||
|
MOVQ R13, R8
|
||||||
|
ADDQ R14, R8
|
||||||
|
MOVQ R14, R9
|
||||||
|
XORQ R10, R10
|
||||||
|
XORQ R11, R11
|
||||||
|
SUBQ R13, R11
|
||||||
|
|
||||||
|
// Loop until CX > BX.
|
||||||
|
blockLoop:
|
||||||
|
round(R8)
|
||||||
|
round(R9)
|
||||||
|
round(R10)
|
||||||
|
round(R11)
|
||||||
|
|
||||||
|
CMPQ CX, BX
|
||||||
|
JLE blockLoop
|
||||||
|
|
||||||
|
MOVQ R8, AX
|
||||||
|
ROLQ $1, AX
|
||||||
|
MOVQ R9, R12
|
||||||
|
ROLQ $7, R12
|
||||||
|
ADDQ R12, AX
|
||||||
|
MOVQ R10, R12
|
||||||
|
ROLQ $12, R12
|
||||||
|
ADDQ R12, AX
|
||||||
|
MOVQ R11, R12
|
||||||
|
ROLQ $18, R12
|
||||||
|
ADDQ R12, AX
|
||||||
|
|
||||||
|
mergeRound(AX, R8)
|
||||||
|
mergeRound(AX, R9)
|
||||||
|
mergeRound(AX, R10)
|
||||||
|
mergeRound(AX, R11)
|
||||||
|
|
||||||
|
JMP afterBlocks
|
||||||
|
|
||||||
|
noBlocks:
|
||||||
|
MOVQ ·prime5v(SB), AX
|
||||||
|
|
||||||
|
afterBlocks:
|
||||||
|
ADDQ DX, AX
|
||||||
|
|
||||||
|
// Right now BX has len(b)-32, and we want to loop until CX > len(b)-8.
|
||||||
|
ADDQ $24, BX
|
||||||
|
|
||||||
|
CMPQ CX, BX
|
||||||
|
JG fourByte
|
||||||
|
|
||||||
|
wordLoop:
|
||||||
|
// Calculate k1.
|
||||||
|
MOVQ (CX), R8
|
||||||
|
ADDQ $8, CX
|
||||||
|
IMULQ R14, R8
|
||||||
|
ROLQ $31, R8
|
||||||
|
IMULQ R13, R8
|
||||||
|
|
||||||
|
XORQ R8, AX
|
||||||
|
ROLQ $27, AX
|
||||||
|
IMULQ R13, AX
|
||||||
|
ADDQ R15, AX
|
||||||
|
|
||||||
|
CMPQ CX, BX
|
||||||
|
JLE wordLoop
|
||||||
|
|
||||||
|
fourByte:
|
||||||
|
ADDQ $4, BX
|
||||||
|
CMPQ CX, BX
|
||||||
|
JG singles
|
||||||
|
|
||||||
|
MOVL (CX), R8
|
||||||
|
ADDQ $4, CX
|
||||||
|
IMULQ R13, R8
|
||||||
|
XORQ R8, AX
|
||||||
|
|
||||||
|
ROLQ $23, AX
|
||||||
|
IMULQ R14, AX
|
||||||
|
ADDQ ·prime3v(SB), AX
|
||||||
|
|
||||||
|
singles:
|
||||||
|
ADDQ $4, BX
|
||||||
|
CMPQ CX, BX
|
||||||
|
JGE finalize
|
||||||
|
|
||||||
|
singlesLoop:
|
||||||
|
MOVBQZX (CX), R12
|
||||||
|
ADDQ $1, CX
|
||||||
|
IMULQ ·prime5v(SB), R12
|
||||||
|
XORQ R12, AX
|
||||||
|
|
||||||
|
ROLQ $11, AX
|
||||||
|
IMULQ R13, AX
|
||||||
|
|
||||||
|
CMPQ CX, BX
|
||||||
|
JL singlesLoop
|
||||||
|
|
||||||
|
finalize:
|
||||||
|
MOVQ AX, R12
|
||||||
|
SHRQ $33, R12
|
||||||
|
XORQ R12, AX
|
||||||
|
IMULQ R14, AX
|
||||||
|
MOVQ AX, R12
|
||||||
|
SHRQ $29, R12
|
||||||
|
XORQ R12, AX
|
||||||
|
IMULQ ·prime3v(SB), AX
|
||||||
|
MOVQ AX, R12
|
||||||
|
SHRQ $32, R12
|
||||||
|
XORQ R12, AX
|
||||||
|
|
||||||
|
MOVQ AX, ret+24(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// writeBlocks uses the same registers as above except that it uses AX to store
|
||||||
|
// the d pointer.
|
||||||
|
|
||||||
|
// func writeBlocks(d *Digest, b []byte) int
|
||||||
|
TEXT ·writeBlocks(SB), NOSPLIT, $0-40
|
||||||
|
// Load fixed primes needed for round.
|
||||||
|
MOVQ ·prime1v(SB), R13
|
||||||
|
MOVQ ·prime2v(SB), R14
|
||||||
|
|
||||||
|
// Load slice.
|
||||||
|
MOVQ b_base+8(FP), CX
|
||||||
|
MOVQ b_len+16(FP), DX
|
||||||
|
LEAQ (CX)(DX*1), BX
|
||||||
|
SUBQ $32, BX
|
||||||
|
|
||||||
|
// Load vN from d.
|
||||||
|
MOVQ d+0(FP), AX
|
||||||
|
MOVQ 0(AX), R8 // v1
|
||||||
|
MOVQ 8(AX), R9 // v2
|
||||||
|
MOVQ 16(AX), R10 // v3
|
||||||
|
MOVQ 24(AX), R11 // v4
|
||||||
|
|
||||||
|
// We don't need to check the loop condition here; this function is
|
||||||
|
// always called with at least one block of data to process.
|
||||||
|
blockLoop:
|
||||||
|
round(R8)
|
||||||
|
round(R9)
|
||||||
|
round(R10)
|
||||||
|
round(R11)
|
||||||
|
|
||||||
|
CMPQ CX, BX
|
||||||
|
JLE blockLoop
|
||||||
|
|
||||||
|
// Copy vN back to d.
|
||||||
|
MOVQ R8, 0(AX)
|
||||||
|
MOVQ R9, 8(AX)
|
||||||
|
MOVQ R10, 16(AX)
|
||||||
|
MOVQ R11, 24(AX)
|
||||||
|
|
||||||
|
// The number of bytes written is CX minus the old base pointer.
|
||||||
|
SUBQ b_base+8(FP), CX
|
||||||
|
MOVQ CX, ret+32(FP)
|
||||||
|
|
||||||
|
RET
|
76
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
Normal file
76
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// +build !amd64 appengine !gc purego
|
||||||
|
|
||||||
|
package xxhash
|
||||||
|
|
||||||
|
// Sum64 computes the 64-bit xxHash digest of b.
|
||||||
|
func Sum64(b []byte) uint64 {
|
||||||
|
// A simpler version would be
|
||||||
|
// d := New()
|
||||||
|
// d.Write(b)
|
||||||
|
// return d.Sum64()
|
||||||
|
// but this is faster, particularly for small inputs.
|
||||||
|
|
||||||
|
n := len(b)
|
||||||
|
var h uint64
|
||||||
|
|
||||||
|
if n >= 32 {
|
||||||
|
v1 := prime1v + prime2
|
||||||
|
v2 := prime2
|
||||||
|
v3 := uint64(0)
|
||||||
|
v4 := -prime1v
|
||||||
|
for len(b) >= 32 {
|
||||||
|
v1 = round(v1, u64(b[0:8:len(b)]))
|
||||||
|
v2 = round(v2, u64(b[8:16:len(b)]))
|
||||||
|
v3 = round(v3, u64(b[16:24:len(b)]))
|
||||||
|
v4 = round(v4, u64(b[24:32:len(b)]))
|
||||||
|
b = b[32:len(b):len(b)]
|
||||||
|
}
|
||||||
|
h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
|
||||||
|
h = mergeRound(h, v1)
|
||||||
|
h = mergeRound(h, v2)
|
||||||
|
h = mergeRound(h, v3)
|
||||||
|
h = mergeRound(h, v4)
|
||||||
|
} else {
|
||||||
|
h = prime5
|
||||||
|
}
|
||||||
|
|
||||||
|
h += uint64(n)
|
||||||
|
|
||||||
|
i, end := 0, len(b)
|
||||||
|
for ; i+8 <= end; i += 8 {
|
||||||
|
k1 := round(0, u64(b[i:i+8:len(b)]))
|
||||||
|
h ^= k1
|
||||||
|
h = rol27(h)*prime1 + prime4
|
||||||
|
}
|
||||||
|
if i+4 <= end {
|
||||||
|
h ^= uint64(u32(b[i:i+4:len(b)])) * prime1
|
||||||
|
h = rol23(h)*prime2 + prime3
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
for ; i < end; i++ {
|
||||||
|
h ^= uint64(b[i]) * prime5
|
||||||
|
h = rol11(h) * prime1
|
||||||
|
}
|
||||||
|
|
||||||
|
h ^= h >> 33
|
||||||
|
h *= prime2
|
||||||
|
h ^= h >> 29
|
||||||
|
h *= prime3
|
||||||
|
h ^= h >> 32
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeBlocks(d *Digest, b []byte) int {
|
||||||
|
v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
|
||||||
|
n := len(b)
|
||||||
|
for len(b) >= 32 {
|
||||||
|
v1 = round(v1, u64(b[0:8:len(b)]))
|
||||||
|
v2 = round(v2, u64(b[8:16:len(b)]))
|
||||||
|
v3 = round(v3, u64(b[16:24:len(b)]))
|
||||||
|
v4 = round(v4, u64(b[24:32:len(b)]))
|
||||||
|
b = b[32:len(b):len(b)]
|
||||||
|
}
|
||||||
|
d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4
|
||||||
|
return n - len(b)
|
||||||
|
}
|
15
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
Normal file
15
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// +build appengine
|
||||||
|
|
||||||
|
// This file contains the safe implementations of otherwise unsafe-using code.
|
||||||
|
|
||||||
|
package xxhash
|
||||||
|
|
||||||
|
// Sum64String computes the 64-bit xxHash digest of s.
|
||||||
|
func Sum64String(s string) uint64 {
|
||||||
|
return Sum64([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteString adds more data to d. It always returns len(s), nil.
|
||||||
|
func (d *Digest) WriteString(s string) (n int, err error) {
|
||||||
|
return d.Write([]byte(s))
|
||||||
|
}
|
46
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
Normal file
46
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
// This file encapsulates usage of unsafe.
|
||||||
|
// xxhash_safe.go contains the safe implementations.
|
||||||
|
|
||||||
|
package xxhash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Notes:
|
||||||
|
//
|
||||||
|
// See https://groups.google.com/d/msg/golang-nuts/dcjzJy-bSpw/tcZYBzQqAQAJ
|
||||||
|
// for some discussion about these unsafe conversions.
|
||||||
|
//
|
||||||
|
// In the future it's possible that compiler optimizations will make these
|
||||||
|
// unsafe operations unnecessary: https://golang.org/issue/2205.
|
||||||
|
//
|
||||||
|
// Both of these wrapper functions still incur function call overhead since they
|
||||||
|
// will not be inlined. We could write Go/asm copies of Sum64 and Digest.Write
|
||||||
|
// for strings to squeeze out a bit more speed. Mid-stack inlining should
|
||||||
|
// eventually fix this.
|
||||||
|
|
||||||
|
// Sum64String computes the 64-bit xxHash digest of s.
|
||||||
|
// It may be faster than Sum64([]byte(s)) by avoiding a copy.
|
||||||
|
func Sum64String(s string) uint64 {
|
||||||
|
var b []byte
|
||||||
|
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
|
||||||
|
bh.Len = len(s)
|
||||||
|
bh.Cap = len(s)
|
||||||
|
return Sum64(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteString adds more data to d. It always returns len(s), nil.
|
||||||
|
// It may be faster than Write([]byte(s)) by avoiding a copy.
|
||||||
|
func (d *Digest) WriteString(s string) (n int, err error) {
|
||||||
|
var b []byte
|
||||||
|
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
|
bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
|
||||||
|
bh.Len = len(s)
|
||||||
|
bh.Cap = len(s)
|
||||||
|
return d.Write(b)
|
||||||
|
}
|
70
vendor/github.com/emicklei/go-restful/.gitignore
generated
vendored
70
vendor/github.com/emicklei/go-restful/.gitignore
generated
vendored
@@ -1,70 +0,0 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
|
|
||||||
restful.html
|
|
||||||
|
|
||||||
*.out
|
|
||||||
|
|
||||||
tmp.prof
|
|
||||||
|
|
||||||
go-restful.test
|
|
||||||
|
|
||||||
examples/restful-basic-authentication
|
|
||||||
|
|
||||||
examples/restful-encoding-filter
|
|
||||||
|
|
||||||
examples/restful-filters
|
|
||||||
|
|
||||||
examples/restful-hello-world
|
|
||||||
|
|
||||||
examples/restful-resource-functions
|
|
||||||
|
|
||||||
examples/restful-serve-static
|
|
||||||
|
|
||||||
examples/restful-user-service
|
|
||||||
|
|
||||||
*.DS_Store
|
|
||||||
examples/restful-user-resource
|
|
||||||
|
|
||||||
examples/restful-multi-containers
|
|
||||||
|
|
||||||
examples/restful-form-handling
|
|
||||||
|
|
||||||
examples/restful-CORS-filter
|
|
||||||
|
|
||||||
examples/restful-options-filter
|
|
||||||
|
|
||||||
examples/restful-curly-router
|
|
||||||
|
|
||||||
examples/restful-cpuprofiler-service
|
|
||||||
|
|
||||||
examples/restful-pre-post-filters
|
|
||||||
|
|
||||||
curly.prof
|
|
||||||
|
|
||||||
examples/restful-NCSA-logging
|
|
||||||
|
|
||||||
examples/restful-html-template
|
|
||||||
|
|
||||||
s.html
|
|
||||||
restful-path-tail
|
|
6
vendor/github.com/emicklei/go-restful/.travis.yml
generated
vendored
6
vendor/github.com/emicklei/go-restful/.travis.yml
generated
vendored
@@ -1,6 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.x
|
|
||||||
|
|
||||||
script: go test -v
|
|
261
vendor/github.com/emicklei/go-restful/CHANGES.md
generated
vendored
261
vendor/github.com/emicklei/go-restful/CHANGES.md
generated
vendored
@@ -1,261 +0,0 @@
|
|||||||
## Change history of go-restful
|
|
||||||
|
|
||||||
|
|
||||||
v2.9.2
|
|
||||||
|
|
||||||
- Reduce allocations in per-request methods to improve performance (#395)
|
|
||||||
|
|
||||||
v2.9.1
|
|
||||||
|
|
||||||
- Fix issue with default responses and invalid status code 0. (#393)
|
|
||||||
|
|
||||||
v2.9.0
|
|
||||||
|
|
||||||
- add per Route content encoding setting (overrides container setting)
|
|
||||||
|
|
||||||
v2.8.0
|
|
||||||
|
|
||||||
- add Request.QueryParameters()
|
|
||||||
- add json-iterator (via build tag)
|
|
||||||
- disable vgo module (until log is moved)
|
|
||||||
|
|
||||||
v2.7.1
|
|
||||||
|
|
||||||
- add vgo module
|
|
||||||
|
|
||||||
v2.6.1
|
|
||||||
|
|
||||||
- add JSONNewDecoderFunc to allow custom JSON Decoder usage (go 1.10+)
|
|
||||||
|
|
||||||
v2.6.0
|
|
||||||
|
|
||||||
- Make JSR 311 routing and path param processing consistent
|
|
||||||
- Adding description to RouteBuilder.Reads()
|
|
||||||
- Update example for Swagger12 and OpenAPI
|
|
||||||
|
|
||||||
2017-09-13
|
|
||||||
|
|
||||||
- added route condition functions using `.If(func)` in route building.
|
|
||||||
|
|
||||||
2017-02-16
|
|
||||||
|
|
||||||
- solved issue #304, make operation names unique
|
|
||||||
|
|
||||||
2017-01-30
|
|
||||||
|
|
||||||
[IMPORTANT] For swagger users, change your import statement to:
|
|
||||||
swagger "github.com/emicklei/go-restful-swagger12"
|
|
||||||
|
|
||||||
- moved swagger 1.2 code to go-restful-swagger12
|
|
||||||
- created TAG 2.0.0
|
|
||||||
|
|
||||||
2017-01-27
|
|
||||||
|
|
||||||
- remove defer request body close
|
|
||||||
- expose Dispatch for testing filters and Routefunctions
|
|
||||||
- swagger response model cannot be array
|
|
||||||
- created TAG 1.0.0
|
|
||||||
|
|
||||||
2016-12-22
|
|
||||||
|
|
||||||
- (API change) Remove code related to caching request content. Removes SetCacheReadEntity(doCache bool)
|
|
||||||
|
|
||||||
2016-11-26
|
|
||||||
|
|
||||||
- Default change! now use CurlyRouter (was RouterJSR311)
|
|
||||||
- Default change! no more caching of request content
|
|
||||||
- Default change! do not recover from panics
|
|
||||||
|
|
||||||
2016-09-22
|
|
||||||
|
|
||||||
- fix the DefaultRequestContentType feature
|
|
||||||
|
|
||||||
2016-02-14
|
|
||||||
|
|
||||||
- take the qualify factor of the Accept header mediatype into account when deciding the contentype of the response
|
|
||||||
- add constructors for custom entity accessors for xml and json
|
|
||||||
|
|
||||||
2015-09-27
|
|
||||||
|
|
||||||
- rename new WriteStatusAnd... to WriteHeaderAnd... for consistency
|
|
||||||
|
|
||||||
2015-09-25
|
|
||||||
|
|
||||||
- fixed problem with changing Header after WriteHeader (issue 235)
|
|
||||||
|
|
||||||
2015-09-14
|
|
||||||
|
|
||||||
- changed behavior of WriteHeader (immediate write) and WriteEntity (no status write)
|
|
||||||
- added support for custom EntityReaderWriters.
|
|
||||||
|
|
||||||
2015-08-06
|
|
||||||
|
|
||||||
- add support for reading entities from compressed request content
|
|
||||||
- use sync.Pool for compressors of http response and request body
|
|
||||||
- add Description to Parameter for documentation in Swagger UI
|
|
||||||
|
|
||||||
2015-03-20
|
|
||||||
|
|
||||||
- add configurable logging
|
|
||||||
|
|
||||||
2015-03-18
|
|
||||||
|
|
||||||
- if not specified, the Operation is derived from the Route function
|
|
||||||
|
|
||||||
2015-03-17
|
|
||||||
|
|
||||||
- expose Parameter creation functions
|
|
||||||
- make trace logger an interface
|
|
||||||
- fix OPTIONSFilter
|
|
||||||
- customize rendering of ServiceError
|
|
||||||
- JSR311 router now handles wildcards
|
|
||||||
- add Notes to Route
|
|
||||||
|
|
||||||
2014-11-27
|
|
||||||
|
|
||||||
- (api add) PrettyPrint per response. (as proposed in #167)
|
|
||||||
|
|
||||||
2014-11-12
|
|
||||||
|
|
||||||
- (api add) ApiVersion(.) for documentation in Swagger UI
|
|
||||||
|
|
||||||
2014-11-10
|
|
||||||
|
|
||||||
- (api change) struct fields tagged with "description" show up in Swagger UI
|
|
||||||
|
|
||||||
2014-10-31
|
|
||||||
|
|
||||||
- (api change) ReturnsError -> Returns
|
|
||||||
- (api add) RouteBuilder.Do(aBuilder) for DRY use of RouteBuilder
|
|
||||||
- fix swagger nested structs
|
|
||||||
- sort Swagger response messages by code
|
|
||||||
|
|
||||||
2014-10-23
|
|
||||||
|
|
||||||
- (api add) ReturnsError allows you to document Http codes in swagger
|
|
||||||
- fixed problem with greedy CurlyRouter
|
|
||||||
- (api add) Access-Control-Max-Age in CORS
|
|
||||||
- add tracing functionality (injectable) for debugging purposes
|
|
||||||
- support JSON parse 64bit int
|
|
||||||
- fix empty parameters for swagger
|
|
||||||
- WebServicesUrl is now optional for swagger
|
|
||||||
- fixed duplicate AccessControlAllowOrigin in CORS
|
|
||||||
- (api change) expose ServeMux in container
|
|
||||||
- (api add) added AllowedDomains in CORS
|
|
||||||
- (api add) ParameterNamed for detailed documentation
|
|
||||||
|
|
||||||
2014-04-16
|
|
||||||
|
|
||||||
- (api add) expose constructor of Request for testing.
|
|
||||||
|
|
||||||
2014-06-27
|
|
||||||
|
|
||||||
- (api add) ParameterNamed gives access to a Parameter definition and its data (for further specification).
|
|
||||||
- (api add) SetCacheReadEntity allow scontrol over whether or not the request body is being cached (default true for compatibility reasons).
|
|
||||||
|
|
||||||
2014-07-03
|
|
||||||
|
|
||||||
- (api add) CORS can be configured with a list of allowed domains
|
|
||||||
|
|
||||||
2014-03-12
|
|
||||||
|
|
||||||
- (api add) Route path parameters can use wildcard or regular expressions. (requires CurlyRouter)
|
|
||||||
|
|
||||||
2014-02-26
|
|
||||||
|
|
||||||
- (api add) Request now provides information about the matched Route, see method SelectedRoutePath
|
|
||||||
|
|
||||||
2014-02-17
|
|
||||||
|
|
||||||
- (api change) renamed parameter constants (go-lint checks)
|
|
||||||
|
|
||||||
2014-01-10
|
|
||||||
|
|
||||||
- (api add) support for CloseNotify, see http://golang.org/pkg/net/http/#CloseNotifier
|
|
||||||
|
|
||||||
2014-01-07
|
|
||||||
|
|
||||||
- (api change) Write* methods in Response now return the error or nil.
|
|
||||||
- added example of serving HTML from a Go template.
|
|
||||||
- fixed comparing Allowed headers in CORS (is now case-insensitive)
|
|
||||||
|
|
||||||
2013-11-13
|
|
||||||
|
|
||||||
- (api add) Response knows how many bytes are written to the response body.
|
|
||||||
|
|
||||||
2013-10-29
|
|
||||||
|
|
||||||
- (api add) RecoverHandler(handler RecoverHandleFunction) to change how panic recovery is handled. Default behavior is to log and return a stacktrace. This may be a security issue as it exposes sourcecode information.
|
|
||||||
|
|
||||||
2013-10-04
|
|
||||||
|
|
||||||
- (api add) Response knows what HTTP status has been written
|
|
||||||
- (api add) Request can have attributes (map of string->interface, also called request-scoped variables
|
|
||||||
|
|
||||||
2013-09-12
|
|
||||||
|
|
||||||
- (api change) Router interface simplified
|
|
||||||
- Implemented CurlyRouter, a Router that does not use|allow regular expressions in paths
|
|
||||||
|
|
||||||
2013-08-05
|
|
||||||
- add OPTIONS support
|
|
||||||
- add CORS support
|
|
||||||
|
|
||||||
2013-08-27
|
|
||||||
|
|
||||||
- fixed some reported issues (see github)
|
|
||||||
- (api change) deprecated use of WriteError; use WriteErrorString instead
|
|
||||||
|
|
||||||
2014-04-15
|
|
||||||
|
|
||||||
- (fix) v1.0.1 tag: fix Issue 111: WriteErrorString
|
|
||||||
|
|
||||||
2013-08-08
|
|
||||||
|
|
||||||
- (api add) Added implementation Container: a WebServices collection with its own http.ServeMux allowing multiple endpoints per program. Existing uses of go-restful will register their services to the DefaultContainer.
|
|
||||||
- (api add) the swagger package has be extended to have a UI per container.
|
|
||||||
- if panic is detected then a small stack trace is printed (thanks to runner-mei)
|
|
||||||
- (api add) WriteErrorString to Response
|
|
||||||
|
|
||||||
Important API changes:
|
|
||||||
|
|
||||||
- (api remove) package variable DoNotRecover no longer works ; use restful.DefaultContainer.DoNotRecover(true) instead.
|
|
||||||
- (api remove) package variable EnableContentEncoding no longer works ; use restful.DefaultContainer.EnableContentEncoding(true) instead.
|
|
||||||
|
|
||||||
|
|
||||||
2013-07-06
|
|
||||||
|
|
||||||
- (api add) Added support for response encoding (gzip and deflate(zlib)). This feature is disabled on default (for backwards compatibility). Use restful.EnableContentEncoding = true in your initialization to enable this feature.
|
|
||||||
|
|
||||||
2013-06-19
|
|
||||||
|
|
||||||
- (improve) DoNotRecover option, moved request body closer, improved ReadEntity
|
|
||||||
|
|
||||||
2013-06-03
|
|
||||||
|
|
||||||
- (api change) removed Dispatcher interface, hide PathExpression
|
|
||||||
- changed receiver names of type functions to be more idiomatic Go
|
|
||||||
|
|
||||||
2013-06-02
|
|
||||||
|
|
||||||
- (optimize) Cache the RegExp compilation of Paths.
|
|
||||||
|
|
||||||
2013-05-22
|
|
||||||
|
|
||||||
- (api add) Added support for request/response filter functions
|
|
||||||
|
|
||||||
2013-05-18
|
|
||||||
|
|
||||||
|
|
||||||
- (api add) Added feature to change the default Http Request Dispatch function (travis cline)
|
|
||||||
- (api change) Moved Swagger Webservice to swagger package (see example restful-user)
|
|
||||||
|
|
||||||
[2012-11-14 .. 2013-05-18>
|
|
||||||
|
|
||||||
- See https://github.com/emicklei/go-restful/commits
|
|
||||||
|
|
||||||
2012-11-14
|
|
||||||
|
|
||||||
- Initial commit
|
|
||||||
|
|
||||||
|
|
7
vendor/github.com/emicklei/go-restful/Makefile
generated
vendored
7
vendor/github.com/emicklei/go-restful/Makefile
generated
vendored
@@ -1,7 +0,0 @@
|
|||||||
all: test
|
|
||||||
|
|
||||||
test:
|
|
||||||
go test -v .
|
|
||||||
|
|
||||||
ex:
|
|
||||||
cd examples && ls *.go | xargs go build -o /tmp/ignore
|
|
88
vendor/github.com/emicklei/go-restful/README.md
generated
vendored
88
vendor/github.com/emicklei/go-restful/README.md
generated
vendored
@@ -1,88 +0,0 @@
|
|||||||
go-restful
|
|
||||||
==========
|
|
||||||
package for building REST-style Web Services using Google Go
|
|
||||||
|
|
||||||
[](https://travis-ci.org/emicklei/go-restful)
|
|
||||||
[](https://goreportcard.com/report/github.com/emicklei/go-restful)
|
|
||||||
[](https://godoc.org/github.com/emicklei/go-restful)
|
|
||||||
|
|
||||||
- [Code examples](https://github.com/emicklei/go-restful/tree/master/examples)
|
|
||||||
|
|
||||||
REST asks developers to use HTTP methods explicitly and in a way that's consistent with the protocol definition. This basic REST design principle establishes a one-to-one mapping between create, read, update, and delete (CRUD) operations and HTTP methods. According to this mapping:
|
|
||||||
|
|
||||||
- GET = Retrieve a representation of a resource
|
|
||||||
- POST = Create if you are sending content to the server to create a subordinate of the specified resource collection, using some server-side algorithm.
|
|
||||||
- PUT = Create if you are sending the full content of the specified resource (URI).
|
|
||||||
- PUT = Update if you are updating the full content of the specified resource.
|
|
||||||
- DELETE = Delete if you are requesting the server to delete the resource
|
|
||||||
- PATCH = Update partial content of a resource
|
|
||||||
- OPTIONS = Get information about the communication options for the request URI
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```Go
|
|
||||||
ws := new(restful.WebService)
|
|
||||||
ws.
|
|
||||||
Path("/users").
|
|
||||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
|
||||||
Produces(restful.MIME_JSON, restful.MIME_XML)
|
|
||||||
|
|
||||||
ws.Route(ws.GET("/{user-id}").To(u.findUser).
|
|
||||||
Doc("get a user").
|
|
||||||
Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
|
|
||||||
Writes(User{}))
|
|
||||||
...
|
|
||||||
|
|
||||||
func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
|
|
||||||
id := request.PathParameter("user-id")
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[Full API of a UserResource](https://github.com/emicklei/go-restful/tree/master/examples/restful-user-resource.go)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- Routes for request → function mapping with path parameter (e.g. {id}) support
|
|
||||||
- Configurable router:
|
|
||||||
- (default) Fast routing algorithm that allows static elements, regular expressions and dynamic parameters in the URL path (e.g. /meetings/{id} or /static/{subpath:*}
|
|
||||||
- Routing algorithm after [JSR311](http://jsr311.java.net/nonav/releases/1.1/spec/spec.html) that is implemented using (but does **not** accept) regular expressions
|
|
||||||
- Request API for reading structs from JSON/XML and accesing parameters (path,query,header)
|
|
||||||
- Response API for writing structs to JSON/XML and setting headers
|
|
||||||
- Customizable encoding using EntityReaderWriter registration
|
|
||||||
- Filters for intercepting the request → response flow on Service or Route level
|
|
||||||
- Request-scoped variables using attributes
|
|
||||||
- Containers for WebServices on different HTTP endpoints
|
|
||||||
- Content encoding (gzip,deflate) of request and response payloads
|
|
||||||
- Automatic responses on OPTIONS (using a filter)
|
|
||||||
- Automatic CORS request handling (using a filter)
|
|
||||||
- API declaration for Swagger UI ([go-restful-openapi](https://github.com/emicklei/go-restful-openapi), see [go-restful-swagger12](https://github.com/emicklei/go-restful-swagger12))
|
|
||||||
- Panic recovery to produce HTTP 500, customizable using RecoverHandler(...)
|
|
||||||
- Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...)
|
|
||||||
- Configurable (trace) logging
|
|
||||||
- Customizable gzip/deflate readers and writers using CompressorProvider registration
|
|
||||||
|
|
||||||
## How to customize
|
|
||||||
There are several hooks to customize the behavior of the go-restful package.
|
|
||||||
|
|
||||||
- Router algorithm
|
|
||||||
- Panic recovery
|
|
||||||
- JSON decoder
|
|
||||||
- Trace logging
|
|
||||||
- Compression
|
|
||||||
- Encoders for other serializers
|
|
||||||
- Use [jsoniter](https://github.com/json-iterator/go) by build this package using a tag, e.g. `go build -tags=jsoniter .`
|
|
||||||
|
|
||||||
TODO: write examples of these.
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
- [Example posted on blog](http://ernestmicklei.com/2012/11/go-restful-first-working-example/)
|
|
||||||
- [Design explained on blog](http://ernestmicklei.com/2012/11/go-restful-api-design/)
|
|
||||||
- [sourcegraph](https://sourcegraph.com/github.com/emicklei/go-restful)
|
|
||||||
- [showcase: Zazkia - tcp proxy for testing resiliency](https://github.com/emicklei/zazkia)
|
|
||||||
- [showcase: Mora - MongoDB REST Api server](https://github.com/emicklei/mora)
|
|
||||||
|
|
||||||
Type ```git shortlog -s``` for a full list of contributors.
|
|
||||||
|
|
||||||
© 2012 - 2018, http://ernestmicklei.com. MIT License. Contributions are welcome.
|
|
1
vendor/github.com/emicklei/go-restful/Srcfile
generated
vendored
1
vendor/github.com/emicklei/go-restful/Srcfile
generated
vendored
@@ -1 +0,0 @@
|
|||||||
{"SkipDirs": ["examples"]}
|
|
10
vendor/github.com/emicklei/go-restful/bench_test.sh
generated
vendored
10
vendor/github.com/emicklei/go-restful/bench_test.sh
generated
vendored
@@ -1,10 +0,0 @@
|
|||||||
#go test -run=none -file bench_test.go -test.bench . -cpuprofile=bench_test.out
|
|
||||||
|
|
||||||
go test -c
|
|
||||||
./go-restful.test -test.run=none -test.cpuprofile=tmp.prof -test.bench=BenchmarkMany
|
|
||||||
./go-restful.test -test.run=none -test.cpuprofile=curly.prof -test.bench=BenchmarkManyCurly
|
|
||||||
|
|
||||||
#go tool pprof go-restful.test tmp.prof
|
|
||||||
go tool pprof go-restful.test curly.prof
|
|
||||||
|
|
||||||
|
|
123
vendor/github.com/emicklei/go-restful/compress.go
generated
vendored
123
vendor/github.com/emicklei/go-restful/compress.go
generated
vendored
@@ -1,123 +0,0 @@
|
|||||||
package restful
|
|
||||||
|
|
||||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
|
||||||
// Use of this source code is governed by a license
|
|
||||||
// that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"compress/gzip"
|
|
||||||
"compress/zlib"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OBSOLETE : use restful.DefaultContainer.EnableContentEncoding(true) to change this setting.
|
|
||||||
var EnableContentEncoding = false
|
|
||||||
|
|
||||||
// CompressingResponseWriter is a http.ResponseWriter that can perform content encoding (gzip and zlib)
|
|
||||||
type CompressingResponseWriter struct {
|
|
||||||
writer http.ResponseWriter
|
|
||||||
compressor io.WriteCloser
|
|
||||||
encoding string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header is part of http.ResponseWriter interface
|
|
||||||
func (c *CompressingResponseWriter) Header() http.Header {
|
|
||||||
return c.writer.Header()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteHeader is part of http.ResponseWriter interface
|
|
||||||
func (c *CompressingResponseWriter) WriteHeader(status int) {
|
|
||||||
c.writer.WriteHeader(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write is part of http.ResponseWriter interface
|
|
||||||
// It is passed through the compressor
|
|
||||||
func (c *CompressingResponseWriter) Write(bytes []byte) (int, error) {
|
|
||||||
if c.isCompressorClosed() {
|
|
||||||
return -1, errors.New("Compressing error: tried to write data using closed compressor")
|
|
||||||
}
|
|
||||||
return c.compressor.Write(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseNotify is part of http.CloseNotifier interface
|
|
||||||
func (c *CompressingResponseWriter) CloseNotify() <-chan bool {
|
|
||||||
return c.writer.(http.CloseNotifier).CloseNotify()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the underlying compressor
|
|
||||||
func (c *CompressingResponseWriter) Close() error {
|
|
||||||
if c.isCompressorClosed() {
|
|
||||||
return errors.New("Compressing error: tried to close already closed compressor")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.compressor.Close()
|
|
||||||
if ENCODING_GZIP == c.encoding {
|
|
||||||
currentCompressorProvider.ReleaseGzipWriter(c.compressor.(*gzip.Writer))
|
|
||||||
}
|
|
||||||
if ENCODING_DEFLATE == c.encoding {
|
|
||||||
currentCompressorProvider.ReleaseZlibWriter(c.compressor.(*zlib.Writer))
|
|
||||||
}
|
|
||||||
// gc hint needed?
|
|
||||||
c.compressor = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CompressingResponseWriter) isCompressorClosed() bool {
|
|
||||||
return nil == c.compressor
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hijack implements the Hijacker interface
|
|
||||||
// This is especially useful when combining Container.EnabledContentEncoding
|
|
||||||
// in combination with websockets (for instance gorilla/websocket)
|
|
||||||
func (c *CompressingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|
||||||
hijacker, ok := c.writer.(http.Hijacker)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, errors.New("ResponseWriter doesn't support Hijacker interface")
|
|
||||||
}
|
|
||||||
return hijacker.Hijack()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
|
|
||||||
func wantsCompressedResponse(httpRequest *http.Request) (bool, string) {
|
|
||||||
header := httpRequest.Header.Get(HEADER_AcceptEncoding)
|
|
||||||
gi := strings.Index(header, ENCODING_GZIP)
|
|
||||||
zi := strings.Index(header, ENCODING_DEFLATE)
|
|
||||||
// use in order of appearance
|
|
||||||
if gi == -1 {
|
|
||||||
return zi != -1, ENCODING_DEFLATE
|
|
||||||
} else if zi == -1 {
|
|
||||||
return gi != -1, ENCODING_GZIP
|
|
||||||
} else {
|
|
||||||
if gi < zi {
|
|
||||||
return true, ENCODING_GZIP
|
|
||||||
}
|
|
||||||
return true, ENCODING_DEFLATE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCompressingResponseWriter create a CompressingResponseWriter for a known encoding = {gzip,deflate}
|
|
||||||
func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding string) (*CompressingResponseWriter, error) {
|
|
||||||
httpWriter.Header().Set(HEADER_ContentEncoding, encoding)
|
|
||||||
c := new(CompressingResponseWriter)
|
|
||||||
c.writer = httpWriter
|
|
||||||
var err error
|
|
||||||
if ENCODING_GZIP == encoding {
|
|
||||||
w := currentCompressorProvider.AcquireGzipWriter()
|
|
||||||
w.Reset(httpWriter)
|
|
||||||
c.compressor = w
|
|
||||||
c.encoding = ENCODING_GZIP
|
|
||||||
} else if ENCODING_DEFLATE == encoding {
|
|
||||||
w := currentCompressorProvider.AcquireZlibWriter()
|
|
||||||
w.Reset(httpWriter)
|
|
||||||
c.compressor = w
|
|
||||||
c.encoding = ENCODING_DEFLATE
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("Unknown encoding:" + encoding)
|
|
||||||
}
|
|
||||||
return c, err
|
|
||||||
}
|
|
103
vendor/github.com/emicklei/go-restful/compressor_cache.go
generated
vendored
103
vendor/github.com/emicklei/go-restful/compressor_cache.go
generated
vendored
@@ -1,103 +0,0 @@
|
|||||||
package restful
|
|
||||||
|
|
||||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
|
||||||
// Use of this source code is governed by a license
|
|
||||||
// that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"compress/gzip"
|
|
||||||
"compress/zlib"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BoundedCachedCompressors is a CompressorProvider that uses a cache with a fixed amount
|
|
||||||
// of writers and readers (resources).
|
|
||||||
// If a new resource is acquired and all are in use, it will return a new unmanaged resource.
|
|
||||||
type BoundedCachedCompressors struct {
|
|
||||||
gzipWriters chan *gzip.Writer
|
|
||||||
gzipReaders chan *gzip.Reader
|
|
||||||
zlibWriters chan *zlib.Writer
|
|
||||||
writersCapacity int
|
|
||||||
readersCapacity int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBoundedCachedCompressors returns a new, with filled cache, BoundedCachedCompressors.
|
|
||||||
func NewBoundedCachedCompressors(writersCapacity, readersCapacity int) *BoundedCachedCompressors {
|
|
||||||
b := &BoundedCachedCompressors{
|
|
||||||
gzipWriters: make(chan *gzip.Writer, writersCapacity),
|
|
||||||
gzipReaders: make(chan *gzip.Reader, readersCapacity),
|
|
||||||
zlibWriters: make(chan *zlib.Writer, writersCapacity),
|
|
||||||
writersCapacity: writersCapacity,
|
|
||||||
readersCapacity: readersCapacity,
|
|
||||||
}
|
|
||||||
for ix := 0; ix < writersCapacity; ix++ {
|
|
||||||
b.gzipWriters <- newGzipWriter()
|
|
||||||
b.zlibWriters <- newZlibWriter()
|
|
||||||
}
|
|
||||||
for ix := 0; ix < readersCapacity; ix++ {
|
|
||||||
b.gzipReaders <- newGzipReader()
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// AcquireGzipWriter returns an resettable *gzip.Writer. Needs to be released.
|
|
||||||
func (b *BoundedCachedCompressors) AcquireGzipWriter() *gzip.Writer {
|
|
||||||
var writer *gzip.Writer
|
|
||||||
select {
|
|
||||||
case writer, _ = <-b.gzipWriters:
|
|
||||||
default:
|
|
||||||
// return a new unmanaged one
|
|
||||||
writer = newGzipWriter()
|
|
||||||
}
|
|
||||||
return writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReleaseGzipWriter accepts a writer (does not have to be one that was cached)
|
|
||||||
// only when the cache has room for it. It will ignore it otherwise.
|
|
||||||
func (b *BoundedCachedCompressors) ReleaseGzipWriter(w *gzip.Writer) {
|
|
||||||
// forget the unmanaged ones
|
|
||||||
if len(b.gzipWriters) < b.writersCapacity {
|
|
||||||
b.gzipWriters <- w
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AcquireGzipReader returns a *gzip.Reader. Needs to be released.
|
|
||||||
func (b *BoundedCachedCompressors) AcquireGzipReader() *gzip.Reader {
|
|
||||||
var reader *gzip.Reader
|
|
||||||
select {
|
|
||||||
case reader, _ = <-b.gzipReaders:
|
|
||||||
default:
|
|
||||||
// return a new unmanaged one
|
|
||||||
reader = newGzipReader()
|
|
||||||
}
|
|
||||||
return reader
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReleaseGzipReader accepts a reader (does not have to be one that was cached)
|
|
||||||
// only when the cache has room for it. It will ignore it otherwise.
|
|
||||||
func (b *BoundedCachedCompressors) ReleaseGzipReader(r *gzip.Reader) {
|
|
||||||
// forget the unmanaged ones
|
|
||||||
if len(b.gzipReaders) < b.readersCapacity {
|
|
||||||
b.gzipReaders <- r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AcquireZlibWriter returns an resettable *zlib.Writer. Needs to be released.
|
|
||||||
func (b *BoundedCachedCompressors) AcquireZlibWriter() *zlib.Writer {
|
|
||||||
var writer *zlib.Writer
|
|
||||||
select {
|
|
||||||
case writer, _ = <-b.zlibWriters:
|
|
||||||
default:
|
|
||||||
// return a new unmanaged one
|
|
||||||
writer = newZlibWriter()
|
|
||||||
}
|
|
||||||
return writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReleaseZlibWriter accepts a writer (does not have to be one that was cached)
|
|
||||||
// only when the cache has room for it. It will ignore it otherwise.
|
|
||||||
func (b *BoundedCachedCompressors) ReleaseZlibWriter(w *zlib.Writer) {
|
|
||||||
// forget the unmanaged ones
|
|
||||||
if len(b.zlibWriters) < b.writersCapacity {
|
|
||||||
b.zlibWriters <- w
|
|
||||||
}
|
|
||||||
}
|
|
91
vendor/github.com/emicklei/go-restful/compressor_pools.go
generated
vendored
91
vendor/github.com/emicklei/go-restful/compressor_pools.go
generated
vendored
@@ -1,91 +0,0 @@
|
|||||||
package restful
|
|
||||||
|
|
||||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
|
||||||
// Use of this source code is governed by a license
|
|
||||||
// that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
|
||||||
"compress/zlib"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SyncPoolCompessors is a CompressorProvider that use the standard sync.Pool.
|
|
||||||
type SyncPoolCompessors struct {
|
|
||||||
GzipWriterPool *sync.Pool
|
|
||||||
GzipReaderPool *sync.Pool
|
|
||||||
ZlibWriterPool *sync.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSyncPoolCompessors returns a new ("empty") SyncPoolCompessors.
|
|
||||||
func NewSyncPoolCompessors() *SyncPoolCompessors {
|
|
||||||
return &SyncPoolCompessors{
|
|
||||||
GzipWriterPool: &sync.Pool{
|
|
||||||
New: func() interface{} { return newGzipWriter() },
|
|
||||||
},
|
|
||||||
GzipReaderPool: &sync.Pool{
|
|
||||||
New: func() interface{} { return newGzipReader() },
|
|
||||||
},
|
|
||||||
ZlibWriterPool: &sync.Pool{
|
|
||||||
New: func() interface{} { return newZlibWriter() },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPoolCompessors) AcquireGzipWriter() *gzip.Writer {
|
|
||||||
return s.GzipWriterPool.Get().(*gzip.Writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPoolCompessors) ReleaseGzipWriter(w *gzip.Writer) {
|
|
||||||
s.GzipWriterPool.Put(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPoolCompessors) AcquireGzipReader() *gzip.Reader {
|
|
||||||
return s.GzipReaderPool.Get().(*gzip.Reader)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPoolCompessors) ReleaseGzipReader(r *gzip.Reader) {
|
|
||||||
s.GzipReaderPool.Put(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPoolCompessors) AcquireZlibWriter() *zlib.Writer {
|
|
||||||
return s.ZlibWriterPool.Get().(*zlib.Writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPoolCompessors) ReleaseZlibWriter(w *zlib.Writer) {
|
|
||||||
s.ZlibWriterPool.Put(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newGzipWriter() *gzip.Writer {
|
|
||||||
// create with an empty bytes writer; it will be replaced before using the gzipWriter
|
|
||||||
writer, err := gzip.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
return writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func newGzipReader() *gzip.Reader {
|
|
||||||
// create with an empty reader (but with GZIP header); it will be replaced before using the gzipReader
|
|
||||||
// we can safely use currentCompressProvider because it is set on package initialization.
|
|
||||||
w := currentCompressorProvider.AcquireGzipWriter()
|
|
||||||
defer currentCompressorProvider.ReleaseGzipWriter(w)
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
w.Reset(b)
|
|
||||||
w.Flush()
|
|
||||||
w.Close()
|
|
||||||
reader, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
return reader
|
|
||||||
}
|
|
||||||
|
|
||||||
func newZlibWriter() *zlib.Writer {
|
|
||||||
writer, err := zlib.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
return writer
|
|
||||||
}
|
|
54
vendor/github.com/emicklei/go-restful/compressors.go
generated
vendored
54
vendor/github.com/emicklei/go-restful/compressors.go
generated
vendored
@@ -1,54 +0,0 @@
|
|||||||
package restful
|
|
||||||
|
|
||||||
// Copyright 2015 Ernest Micklei. All rights reserved.
|
|
||||||
// Use of this source code is governed by a license
|
|
||||||
// that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"compress/gzip"
|
|
||||||
"compress/zlib"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CompressorProvider describes a component that can provider compressors for the std methods.
|
|
||||||
type CompressorProvider interface {
|
|
||||||
// Returns a *gzip.Writer which needs to be released later.
|
|
||||||
// Before using it, call Reset().
|
|
||||||
AcquireGzipWriter() *gzip.Writer
|
|
||||||
|
|
||||||
// Releases an acquired *gzip.Writer.
|
|
||||||
ReleaseGzipWriter(w *gzip.Writer)
|
|
||||||
|
|
||||||
// Returns a *gzip.Reader which needs to be released later.
|
|
||||||
AcquireGzipReader() *gzip.Reader
|
|
||||||
|
|
||||||
// Releases an acquired *gzip.Reader.
|
|
||||||
ReleaseGzipReader(w *gzip.Reader)
|
|
||||||
|
|
||||||
// Returns a *zlib.Writer which needs to be released later.
|
|
||||||
// Before using it, call Reset().
|
|
||||||
AcquireZlibWriter() *zlib.Writer
|
|
||||||
|
|
||||||
// Releases an acquired *zlib.Writer.
|
|
||||||
ReleaseZlibWriter(w *zlib.Writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultCompressorProvider is the actual provider of compressors (zlib or gzip).
|
|
||||||
var currentCompressorProvider CompressorProvider
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
currentCompressorProvider = NewSyncPoolCompessors()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CurrentCompressorProvider returns the current CompressorProvider.
|
|
||||||
// It is initialized using a SyncPoolCompessors.
|
|
||||||
func CurrentCompressorProvider() CompressorProvider {
|
|
||||||
return currentCompressorProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCompressorProvider sets the actual provider of compressors (zlib or gzip).
|
|
||||||
func SetCompressorProvider(p CompressorProvider) {
|
|
||||||
if p == nil {
|
|
||||||
panic("cannot set compressor provider to nil")
|
|
||||||
}
|
|
||||||
currentCompressorProvider = p
|
|
||||||
}
|
|
30
vendor/github.com/emicklei/go-restful/constants.go
generated
vendored
30
vendor/github.com/emicklei/go-restful/constants.go
generated
vendored
@@ -1,30 +0,0 @@
|
|||||||
package restful
|
|
||||||
|
|
||||||
// Copyright 2013 Ernest Micklei. All rights reserved.
|
|
||||||
// Use of this source code is governed by a license
|
|
||||||
// that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
const (
|
|
||||||
MIME_XML = "application/xml" // Accept or Content-Type used in Consumes() and/or Produces()
|
|
||||||
MIME_JSON = "application/json" // Accept or Content-Type used in Consumes() and/or Produces()
|
|
||||||
MIME_OCTET = "application/octet-stream" // If Content-Type is not present in request, use the default
|
|
||||||
|
|
||||||
HEADER_Allow = "Allow"
|
|
||||||
HEADER_Accept = "Accept"
|
|
||||||
HEADER_Origin = "Origin"
|
|
||||||
HEADER_ContentType = "Content-Type"
|
|
||||||
HEADER_LastModified = "Last-Modified"
|
|
||||||
HEADER_AcceptEncoding = "Accept-Encoding"
|
|
||||||
HEADER_ContentEncoding = "Content-Encoding"
|
|
||||||
HEADER_AccessControlExposeHeaders = "Access-Control-Expose-Headers"
|
|
||||||
HEADER_AccessControlRequestMethod = "Access-Control-Request-Method"
|
|
||||||
HEADER_AccessControlRequestHeaders = "Access-Control-Request-Headers"
|
|
||||||
HEADER_AccessControlAllowMethods = "Access-Control-Allow-Methods"
|
|
||||||
HEADER_AccessControlAllowOrigin = "Access-Control-Allow-Origin"
|
|
||||||
HEADER_AccessControlAllowCredentials = "Access-Control-Allow-Credentials"
|
|
||||||
HEADER_AccessControlAllowHeaders = "Access-Control-Allow-Headers"
|
|
||||||
HEADER_AccessControlMaxAge = "Access-Control-Max-Age"
|
|
||||||
|
|
||||||
ENCODING_GZIP = "gzip"
|
|
||||||
ENCODING_DEFLATE = "deflate"
|
|
||||||
)
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user