Merge branch 'traffic' of github.com:coredns/coredns into traffic

This commit is contained in:
Miek Gieben
2020-01-18 07:22:32 +01:00
45 changed files with 356 additions and 284 deletions

View File

@@ -88,6 +88,8 @@ build:
mkdir -p build/windows/amd64 && $(MAKE) coredns BINARY=build/windows/amd64/$(NAME).exe SYSTEM="GOOS=windows GOARCH=amd64" CHECKS="" BUILDOPTS="" mkdir -p build/windows/amd64 && $(MAKE) coredns BINARY=build/windows/amd64/$(NAME).exe SYSTEM="GOOS=windows GOARCH=amd64" CHECKS="" BUILDOPTS=""
@echo Building: linux/mips - $(VERSION) @echo Building: linux/mips - $(VERSION)
mkdir -p build/linux/mips && $(MAKE) coredns BINARY=build/linux/mips/$(NAME) SYSTEM="GOOS=linux GOARCH=mips" CHECKS="" BUILDOPTS="" mkdir -p build/linux/mips && $(MAKE) coredns BINARY=build/linux/mips/$(NAME) SYSTEM="GOOS=linux GOARCH=mips" CHECKS="" BUILDOPTS=""
@echo Building: linux/mips64le - $(VERSION)
mkdir -p build/linux/mips64le && $(MAKE) coredns BINARY=build/linux/mips64le/$(NAME) SYSTEM="GOOS=linux GOARCH=mips64le" CHECKS="" BUILDOPTS=""
@echo Building: linux/$(LINUX_ARCH) - $(VERSION) ;\ @echo Building: linux/$(LINUX_ARCH) - $(VERSION) ;\
for arch in $(LINUX_ARCH); do \ for arch in $(LINUX_ARCH); do \
mkdir -p build/linux/$$arch && $(MAKE) coredns BINARY=build/linux/$$arch/$(NAME) SYSTEM="GOOS=linux GOARCH=$$arch" CHECKS="" BUILDOPTS="" ;\ mkdir -p build/linux/$$arch && $(MAKE) coredns BINARY=build/linux/$$arch/$(NAME) SYSTEM="GOOS=linux GOARCH=$$arch" CHECKS="" BUILDOPTS="" ;\
@@ -100,6 +102,7 @@ tar:
tar -zcf release/$(NAME)_$(VERSION)_darwin_amd64.tgz -C build/darwin/amd64 $(NAME) tar -zcf release/$(NAME)_$(VERSION)_darwin_amd64.tgz -C build/darwin/amd64 $(NAME)
tar -zcf release/$(NAME)_$(VERSION)_windows_amd64.tgz -C build/windows/amd64 $(NAME).exe tar -zcf release/$(NAME)_$(VERSION)_windows_amd64.tgz -C build/windows/amd64 $(NAME).exe
tar -zcf release/$(NAME)_$(VERSION)_linux_mips.tgz -C build/linux/mips $(NAME) tar -zcf release/$(NAME)_$(VERSION)_linux_mips.tgz -C build/linux/mips $(NAME)
tar -zcf release/$(NAME)_$(VERSION)_linux_mips64le.tgz -C build/linux/mips64le $(NAME)
for arch in $(LINUX_ARCH); do \ for arch in $(LINUX_ARCH); do \
tar -zcf release/$(NAME)_$(VERSION)_linux_$$arch.tgz -C build/linux/$$arch $(NAME) ;\ tar -zcf release/$(NAME)_$(VERSION)_linux_$$arch.tgz -C build/linux/$$arch $(NAME) ;\
done done

View File

@@ -77,25 +77,20 @@ The above command alone will have `coredns` binary generated.
## Examples ## Examples
When starting CoreDNS without any configuration, it loads the When starting CoreDNS without any configuration, it loads the
[*whoami*](https://coredns.io/plugins/whoami) plugin and starts listening on port 53 (override with [*whoami*](https://coredns.io/plugins/whoami) and [*log*](https://coredns.io/plugins/log) plugins
`-dns.port`), it should show the following: and starts listening on port 53 (override with `-dns.port`), it should show the following:
~~~ txt ~~~ txt
.:53 .:53
______ ____ _ _______ CoreDNS-1.6.6
/ ____/___ ________ / __ \/ | / / ___/ ~ CoreDNS-1.6.3 linux/amd64, go1.13.5, aa8c32
/ / / __ \/ ___/ _ \/ / / / |/ /\__ \ ~ linux/amd64, go1.13,
/ /___/ /_/ / / / __/ /_/ / /| /___/ /
\____/\____/_/ \___/_____/_/ |_//____/
~~~ ~~~
Any query sent to port 53 should return some information; your sending address, port and protocol Any query sent to port 53 should return some information; your sending address, port and protocol
used. used. The query should also be logged to standard output.
If you have a Corefile without a port number specified it will, by default, use port 53, but you can If you have a Corefile without a port number specified it will, by default, use port 53, but you can
override the port with the `-dns.port` flag: override the port with the `-dns.port` flag: `coredns -dns.port 1053`, runs the server on port 1053.
`./coredns -dns.port 1053`, runs the server on port 1053.
Start a simple proxy. You'll need to be root to start listening on port 53. Start a simple proxy. You'll need to be root to start listening on port 53.
@@ -108,11 +103,11 @@ Start a simple proxy. You'll need to be root to start listening on port 53.
} }
~~~ ~~~
Just start CoreDNS: `./coredns`. Then just query on that port (53). The query should be forwarded Start CoreDNS and then query on that port (53). The query should be forwarded to 8.8.8.8 and the
to 8.8.8.8 and the response will be returned. Each query should also show up in the log which is response will be returned. Each query should also show up in the log which is printed on standard
printed on standard output. output.
Serve the (NSEC) DNSSEC-signed `example.org` on port 1053, with errors and logging sent to standard To serve the (NSEC) DNSSEC-signed `example.org` on port 1053, with errors and logging sent to standard
output. Allow zone transfers to everybody, but specifically mention 1 IP address so that CoreDNS can output. Allow zone transfers to everybody, but specifically mention 1 IP address so that CoreDNS can
send notifies to it. send notifies to it.
@@ -139,6 +134,7 @@ example.org:1053 {
errors errors
log log
} }
. { . {
any any
forward . 8.8.8.8:53 forward . 8.8.8.8:53

11
go.mod
View File

@@ -7,10 +7,10 @@ require (
github.com/Azure/azure-sdk-for-go v32.6.0+incompatible github.com/Azure/azure-sdk-for-go v32.6.0+incompatible
github.com/Azure/go-autorest/autorest v0.9.3 github.com/Azure/go-autorest/autorest v0.9.3
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 github.com/Azure/go-autorest/autorest/azure/auth v0.4.2
github.com/DataDog/datadog-go v2.2.0+incompatible // indirect github.com/DataDog/datadog-go v3.3.1+incompatible // indirect
github.com/Shopify/sarama v1.21.0 // indirect github.com/Shopify/sarama v1.21.0 // indirect
github.com/apache/thrift v0.13.0 // indirect github.com/apache/thrift v0.13.0 // indirect
github.com/aws/aws-sdk-go v1.27.0 github.com/aws/aws-sdk-go v1.28.0
github.com/caddyserver/caddy v1.0.4 github.com/caddyserver/caddy v1.0.4
github.com/coredns/federation v0.0.0-20190818181423-e032b096babe github.com/coredns/federation v0.0.0-20190818181423-e032b096babe
github.com/coreos/go-systemd v0.0.0-20190212144455-93d5ec2c7f76 // indirect github.com/coreos/go-systemd v0.0.0-20190212144455-93d5ec2c7f76 // indirect
@@ -42,14 +42,9 @@ require (
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f golang.org/x/sys v0.0.0-20191220142924-d4481acd189f
google.golang.org/api v0.15.0 google.golang.org/api v0.15.0
google.golang.org/grpc v1.26.0 google.golang.org/grpc v1.26.0
gopkg.in/DataDog/dd-trace-go.v1 v1.19.0 gopkg.in/DataDog/dd-trace-go.v1 v1.20.0
k8s.io/api v0.17.0 k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0 k8s.io/apimachinery v0.17.0
k8s.io/client-go v0.17.0 k8s.io/client-go v0.17.0
k8s.io/klog v1.0.0 k8s.io/klog v1.0.0
) )
replace (
github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.0.0+incompatible
github.com/miekg/dns v1.1.3 => github.com/miekg/dns v1.1.22
)

13
go.sum
View File

@@ -41,8 +41,8 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v2.2.0+incompatible h1:V5BKkxACZLjzHjSgBbr2gvLA2Ae49yhc6CSY7MLy5k4= github.com/DataDog/datadog-go v3.3.1+incompatible h1:NT/ghvYzqIzTJGiqvc3n4t9cZy8waO+I2O3I8Cok6/k=
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.3.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.3.5 h1:DtpNbljikUepEPD16hD4LvIcmhnhdLTiW/5pHgbmp14= github.com/DataDog/zstd v1.3.5 h1:DtpNbljikUepEPD16hD4LvIcmhnhdLTiW/5pHgbmp14=
github.com/DataDog/zstd v1.3.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.3.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
@@ -66,8 +66,8 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.0 h1:0xphMHGMLBrPMfxR2AmVjZKcMEESEgWF8Kru94BNByk= github.com/aws/aws-sdk-go v1.28.0 h1:NkmnHFVEMTRYTleRLm5xUaL1mHKKkYQl4rCd+jzD58c=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.28.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
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.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -388,6 +388,7 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 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/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -566,8 +567,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/DataDog/dd-trace-go.v1 v1.19.0 h1:aFSFd6oDMdvPYiToGqTv7/ERA6QrPhGaXSuueRCaM88= gopkg.in/DataDog/dd-trace-go.v1 v1.20.0 h1:OUvLkkEtg2HpDS9g+GeNKDnJtx9zVbqCh2hGH7jHHfg=
gopkg.in/DataDog/dd-trace-go.v1 v1.19.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg= gopkg.in/DataDog/dd-trace-go.v1 v1.20.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-ACL" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-ACL" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.PP .PP
\fIacl\fP - enforces access control policies on source ip and prevents unauthorized access to DNS servers. \fIacl\fP - enforces access control policies on source ip and prevents unauthorized access to DNS servers.

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-AUTOPATH" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-AUTOPATH" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-BIND" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-BIND" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-BUFSIZE" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-BUFSIZE" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-CACHE" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-CACHE" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-CLOUDDNS" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-CLOUDDNS" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-DNSSEC" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-DNSSEC" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-FILE" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-FILE" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-FORWARD" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-FORWARD" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-GRPC" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-GRPC" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-HEALTH" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-HEALTH" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-HOSTS" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-HOSTS" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-IMPORT" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-IMPORT" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-KUBERNETES" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-KUBERNETES" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-RELOAD" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-RELOAD" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-REWRITE" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-REWRITE" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-ROUTE53" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-ROUTE53" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-SECONDARY" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-SECONDARY" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-SIGN" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-SIGN" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP
@@ -43,7 +43,7 @@ the signature only has 14 days left before expiring.
Both these dates are only checked on the SOA's signature(s). Both these dates are only checked on the SOA's signature(s).
.IP \(bu 4 .IP \(bu 4
Create RRSIGs that have an inception of -3 hours (minus a jitter between 0 and 18 hours) Create RRSIGs that have an inception of -3 hours (minus a jitter between 0 and 18 hours)
and a expiration of +32 days for every given DNSKEY. and a expiration of +32 (plus a jitter between 0 and 5 days) days for every given DNSKEY.
.IP \(bu 4 .IP \(bu 4
Add NSEC records for all names in the zone. The TTL for these is the negative cache TTL from the Add NSEC records for all names in the zone. The TTL for these is the negative cache TTL from the
SOA record. SOA record.

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-TEMPLATE" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-TEMPLATE" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-TLS" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-TLS" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -1,5 +1,5 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl .\" Generated by Mmark Markdown Processer - mmark.miek.nl
.TH "COREDNS-TRANSFER" 7 "December 2019" "CoreDNS" "CoreDNS Plugins" .TH "COREDNS-TRANSFER" 7 "January 2020" "CoreDNS" "CoreDNS Plugins"
.SH "NAME" .SH "NAME"
.PP .PP

View File

@@ -178,7 +178,7 @@ func (e *Etcd) TTL(kv *mvccpb.KeyValue, serv *msg.Service) uint32 {
// shouldInclude returns true if the service should be included in a list of records, given the qType. For all the // shouldInclude returns true if the service should be included in a list of records, given the qType. For all the
// currently supported lookup types, the only one to allow for an empty Host field in the service are TXT records // currently supported lookup types, the only one to allow for an empty Host field in the service are TXT records
// which resolve directly. If a TXT record is being resolved by CNAME, then we expect the Host field to have a // which resolve directly. If a TXT record is being resolved by CNAME, then we expect the Host field to have a
// value while the TXT field will be empty. // value while the TXT field will be empty.
func shouldInclude(serv *msg.Service, qType uint16) bool { func shouldInclude(serv *msg.Service, qType uint16) bool {
return (qType == dns.TypeTXT && serv.Text != "") || serv.Host != "" return (qType == dns.TypeTXT && serv.Text != "") || serv.Host != ""

View File

@@ -12,8 +12,8 @@ import (
"github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/debug" "github.com/coredns/coredns/plugin/debug"
"github.com/coredns/coredns/plugin/pkg/policy"
clog "github.com/coredns/coredns/plugin/pkg/log" clog "github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/plugin/pkg/policy"
"github.com/coredns/coredns/request" "github.com/coredns/coredns/request"
"github.com/miekg/dns" "github.com/miekg/dns"

View File

@@ -8,8 +8,8 @@ import (
"github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/metrics" "github.com/coredns/coredns/plugin/metrics"
"github.com/coredns/coredns/plugin/pkg/policy"
"github.com/coredns/coredns/plugin/pkg/parse" "github.com/coredns/coredns/plugin/pkg/parse"
"github.com/coredns/coredns/plugin/pkg/policy"
pkgtls "github.com/coredns/coredns/plugin/pkg/tls" pkgtls "github.com/coredns/coredns/plugin/pkg/tls"
"github.com/coredns/coredns/plugin/pkg/transport" "github.com/coredns/coredns/plugin/pkg/transport"

View File

@@ -7,10 +7,10 @@
## Description ## Description
By just using *log* you dump all queries (and parts for the reply) on standard output. Options exist By just using *log* you dump all queries (and parts for the reply) on standard output. Options exist
to tweak the output a little. The date/time prefix on log lines is RFC3339 formatted with to tweak the output a little. Note that for busy servers logging will incur a performance hit.
milliseconds.
Note that for busy servers logging will incur a performance hit. Enabling or disabling the *log* plugin only affects the query logging, any other logging from
CoreDNS will show up regardless.
## Syntax ## Syntax
@@ -18,8 +18,7 @@ Note that for busy servers logging will incur a performance hit.
log log
~~~ ~~~
* With no arguments, a query log entry is written to *stdout* in the common log format for all requests With no arguments, a query log entry is written to *stdout* in the common log format for all requests.
Or if you want/need slightly more control: Or if you want/need slightly more control:
~~~ txt ~~~ txt
@@ -47,11 +46,11 @@ The classes of responses have the following meaning:
* `denial`: either NXDOMAIN or nodata responses (Name exists, type does not). A nodata response * `denial`: either NXDOMAIN or nodata responses (Name exists, type does not). A nodata response
sets the return code to NOERROR. sets the return code to NOERROR.
* `error`: SERVFAIL, NOTIMP, REFUSED, etc. Anything that indicates the remote server is not willing to * `error`: SERVFAIL, NOTIMP, REFUSED, etc. Anything that indicates the remote server is not willing to
resolve the request. resolve the request.
* `all`: the default - nothing is specified. Using of this class means that all messages will be * `all`: the default - nothing is specified. Using of this class means that all messages will be
logged whatever we mix together with "all". logged whatever we mix together with "all".
If no class is specified, it defaults to *all*. If no class is specified, it defaults to `all`.
## Log Format ## Log Format

View File

@@ -4,12 +4,23 @@ import (
"fmt" "fmt"
"net" "net"
"os" "os"
"strings"
"github.com/coredns/coredns/plugin/pkg/transport" "github.com/coredns/coredns/plugin/pkg/transport"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
// Strips the zone, but preserves any port that comes after the zone
func stripZone(host string) string {
if strings.Contains(host, "%") {
lastPercent := strings.LastIndex(host, "%")
newHost := host[:lastPercent]
return newHost
}
return host
}
// HostPortOrFile parses the strings in s, each string can either be a // HostPortOrFile parses the strings in s, each string can either be a
// address, [scheme://]address:port or a filename. The address part is checked // address, [scheme://]address:port or a filename. The address part is checked
// and in case of filename a resolv.conf like file is (assumed) and parsed and // and in case of filename a resolv.conf like file is (assumed) and parsed and
@@ -21,10 +32,11 @@ func HostPortOrFile(s ...string) ([]string, error) {
trans, host := Transport(h) trans, host := Transport(h)
addr, _, err := net.SplitHostPort(host) addr, _, err := net.SplitHostPort(host)
if err != nil { if err != nil {
// Parse didn't work, it is not a addr:port combo // Parse didn't work, it is not a addr:port combo
if net.ParseIP(host) == nil { hostNoZone := stripZone(host)
// Not an IP address. if net.ParseIP(hostNoZone) == nil {
ss, err := tryFile(host) ss, err := tryFile(host)
if err == nil { if err == nil {
servers = append(servers, ss...) servers = append(servers, ss...)
@@ -47,8 +59,7 @@ func HostPortOrFile(s ...string) ([]string, error) {
continue continue
} }
if net.ParseIP(addr) == nil { if net.ParseIP(stripZone(addr)) == nil {
// Not an IP address.
ss, err := tryFile(host) ss, err := tryFile(host)
if err == nil { if err == nil {
servers = append(servers, ss...) servers = append(servers, ss...)

View File

@@ -34,6 +34,26 @@ func TestHostPortOrFile(t *testing.T) {
"127.0.0.1:53", "127.0.0.1:53",
false, false,
}, },
{
"fe80::1",
"[fe80::1]:53",
false,
},
{
"fe80::1%ens3",
"[fe80::1%ens3]:53",
false,
},
{
"[fd01::1]:153",
"[fd01::1]:153",
false,
},
{
"[fd01::1%ens3]:153",
"[fd01::1%ens3]:153",
false,
},
} }
err := ioutil.WriteFile("resolv.conf", []byte("nameserver 127.0.0.1\n"), 0600) err := ioutil.WriteFile("resolv.conf", []byte("nameserver 127.0.0.1\n"), 0600)

View File

@@ -11,8 +11,8 @@ import (
"sync" "sync"
clog "github.com/coredns/coredns/plugin/pkg/log" clog "github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/plugin/pkg/uniq"
"github.com/coredns/coredns/plugin/pkg/reuseport" "github.com/coredns/coredns/plugin/pkg/reuseport"
"github.com/coredns/coredns/plugin/pkg/uniq"
) )
var ( var (

View File

@@ -32,7 +32,7 @@ it do key or algorithm rollovers - it just signs.
Both these dates are only checked on the SOA's signature(s). Both these dates are only checked on the SOA's signature(s).
* Create RRSIGs that have an inception of -3 hours (minus a jitter between 0 and 18 hours) * Create RRSIGs that have an inception of -3 hours (minus a jitter between 0 and 18 hours)
and a expiration of +32 days for every given DNSKEY. and a expiration of +32 (plus a jitter between 0 and 5 days) days for every given DNSKEY.
* Add NSEC records for all names in the zone. The TTL for these is the negative cache TTL from the * Add NSEC records for all names in the zone. The TTL for these is the negative cache TTL from the
SOA record. SOA record.

View File

@@ -23,7 +23,7 @@ func setup(c *caddy.Controller) error {
c.OnStartup(sign.OnStartup) c.OnStartup(sign.OnStartup)
c.OnStartup(func() error { c.OnStartup(func() error {
for _, signer := range sign.signers { for _, signer := range sign.signers {
go signer.refresh(DurationRefreshHours) go signer.refresh(durationRefreshHours)
} }
return nil return nil
}) })
@@ -64,12 +64,13 @@ func parse(c *caddy.Controller) (*Sign, error) {
signers := make([]*Signer, len(origins)) signers := make([]*Signer, len(origins))
for i := range origins { for i := range origins {
signers[i] = &Signer{ signers[i] = &Signer{
dbfile: dbfile, dbfile: dbfile,
origin: plugin.Host(origins[i]).Normalize(), origin: plugin.Host(origins[i]).Normalize(),
jitter: time.Duration(float32(DurationJitter) * rand.Float32()), jitterIncep: time.Duration(float32(durationInceptionJitter) * rand.Float32()),
directory: "/var/lib/coredns", jitterExpir: time.Duration(float32(durationExpirationDayJitter) * rand.Float32()),
stop: make(chan struct{}), directory: "/var/lib/coredns",
signedfile: fmt.Sprintf("db.%ssigned", origins[i]), // origins[i] is a fqdn, so it ends with a dot, hence %ssigned. stop: make(chan struct{}),
signedfile: fmt.Sprintf("db.%ssigned", origins[i]), // origins[i] is a fqdn, so it ends with a dot, hence %ssigned.
} }
} }

View File

@@ -26,12 +26,13 @@ func (s *Sign) OnStartup() error {
// Various duration constants for signing of the zones. // Various duration constants for signing of the zones.
const ( const (
DurationExpireDays = 7 * 24 * time.Hour // max time allowed before expiration durationExpireDays = 7 * 24 * time.Hour // max time allowed before expiration
DurationResignDays = 6 * 24 * time.Hour // if the last sign happenend this long ago, sign again durationResignDays = 6 * 24 * time.Hour // if the last sign happenend this long ago, sign again
DurationSignatureExpireDays = 32 * 24 * time.Hour // sign for 32 days durationSignatureExpireDays = 32 * 24 * time.Hour // sign for 32 days
DurationRefreshHours = 5 * time.Hour // check zones every 5 hours durationRefreshHours = 5 * time.Hour // check zones every 5 hours
DurationJitter = -18 * time.Hour // default max jitter durationInceptionJitter = -18 * time.Hour // default max jitter for the inception
DurationSignatureInceptionHours = -3 * time.Hour // -(2+1) hours, be sure to catch daylight saving time and such, jitter is subtracted durationExpirationDayJitter = 5 * 24 * time.Hour // default max jitter for the expiration
durationSignatureInceptionHours = -3 * time.Hour // -(2+1) hours, be sure to catch daylight saving time and such, jitter is subtracted
) )
const timeFmt = "2006-01-02T15:04:05.000Z07:00" const timeFmt = "2006-01-02T15:04:05.000Z07:00"

View File

@@ -18,11 +18,12 @@ var log = clog.NewWithPlugin("sign")
// Signer holds the data needed to sign a zone file. // Signer holds the data needed to sign a zone file.
type Signer struct { type Signer struct {
keys []Pair keys []Pair
origin string origin string
dbfile string dbfile string
directory string directory string
jitter time.Duration jitterIncep time.Duration
jitterExpir time.Duration
signedfile string signedfile string
stop chan struct{} stop chan struct{}
@@ -42,7 +43,7 @@ func (s *Signer) Sign(now time.Time) (*file.Zone, error) {
mttl := z.Apex.SOA.Minttl mttl := z.Apex.SOA.Minttl
ttl := z.Apex.SOA.Header().Ttl ttl := z.Apex.SOA.Header().Ttl
inception, expiration := lifetime(now, s.jitter) inception, expiration := lifetime(now, s.jitterIncep, s.jitterExpir)
z.Apex.SOA.Serial = uint32(now.Unix()) z.Apex.SOA.Serial = uint32(now.Unix())
for _, pair := range s.keys { for _, pair := range s.keys {
@@ -143,8 +144,8 @@ func resign(rd io.Reader, now time.Time) (why error) {
} }
incep, _ := time.Parse("20060102150405", dns.TimeToString(x.Inception)) incep, _ := time.Parse("20060102150405", dns.TimeToString(x.Inception))
// If too long ago, resign. // If too long ago, resign.
if now.Sub(incep) >= 0 && now.Sub(incep) > DurationResignDays { if now.Sub(incep) >= 0 && now.Sub(incep) > durationResignDays {
return fmt.Errorf("inception %q was more than: %s ago from %s: %s", incep.Format(timeFmt), DurationResignDays, now.Format(timeFmt), now.Sub(incep)) return fmt.Errorf("inception %q was more than: %s ago from %s: %s", incep.Format(timeFmt), durationResignDays, now.Format(timeFmt), now.Sub(incep))
} }
// Inception hasn't even start yet. // Inception hasn't even start yet.
if now.Sub(incep) < 0 { if now.Sub(incep) < 0 {
@@ -152,8 +153,8 @@ func resign(rd io.Reader, now time.Time) (why error) {
} }
expire, _ := time.Parse("20060102150405", dns.TimeToString(x.Expiration)) expire, _ := time.Parse("20060102150405", dns.TimeToString(x.Expiration))
if expire.Sub(now) < DurationExpireDays { if expire.Sub(now) < durationExpireDays {
return fmt.Errorf("expiration %q is less than: %s away from %s: %s", expire.Format(timeFmt), DurationExpireDays, now.Format(timeFmt), expire.Sub(now)) return fmt.Errorf("expiration %q is less than: %s away from %s: %s", expire.Format(timeFmt), durationExpireDays, now.Format(timeFmt), expire.Sub(now))
} }
} }
i++ i++
@@ -173,7 +174,7 @@ func signAndLog(s *Signer, why error) {
z, err := s.Sign(now) z, err := s.Sign(now)
log.Infof("Signing %q because %s", s.origin, why) log.Infof("Signing %q because %s", s.origin, why)
if err != nil { if err != nil {
log.Warningf("Error signing %q with key tags %q in %s: %s, next: %s", s.origin, keyTag(s.keys), time.Since(now), err, now.Add(DurationRefreshHours).Format(timeFmt)) log.Warningf("Error signing %q with key tags %q in %s: %s, next: %s", s.origin, keyTag(s.keys), time.Since(now), err, now.Add(durationRefreshHours).Format(timeFmt))
return return
} }
@@ -181,7 +182,7 @@ func signAndLog(s *Signer, why error) {
log.Warningf("Error signing %q: failed to move zone file into place: %s", s.origin, err) log.Warningf("Error signing %q: failed to move zone file into place: %s", s.origin, err)
return return
} }
log.Infof("Successfully signed zone %q in %q with key tags %q and %d SOA serial, elapsed %f, next: %s", s.origin, filepath.Join(s.directory, s.signedfile), keyTag(s.keys), z.Apex.SOA.Serial, time.Since(now).Seconds(), now.Add(DurationRefreshHours).Format(timeFmt)) log.Infof("Successfully signed zone %q in %q with key tags %q and %d SOA serial, elapsed %f, next: %s", s.origin, filepath.Join(s.directory, s.signedfile), keyTag(s.keys), z.Apex.SOA.Serial, time.Since(now).Seconds(), now.Add(durationRefreshHours).Format(timeFmt))
} }
// refresh checks every val if some zones need to be resigned. // refresh checks every val if some zones need to be resigned.
@@ -202,8 +203,8 @@ func (s *Signer) refresh(val time.Duration) {
} }
} }
func lifetime(now time.Time, jitter time.Duration) (uint32, uint32) { func lifetime(now time.Time, jitterInception, jitterExpiration time.Duration) (uint32, uint32) {
incep := uint32(now.Add(DurationSignatureInceptionHours).Add(jitter).Unix()) incep := uint32(now.Add(durationSignatureInceptionHours).Add(jitterInception).Unix())
expir := uint32(now.Add(DurationSignatureExpireDays).Unix()) expir := uint32(now.Add(durationSignatureExpireDays).Add(jitterExpiration).Unix())
return incep, expir return incep, expir
} }

View File

@@ -1,4 +1,4 @@
Hacking on *traffic* # Hacking on *traffic*
Repos used: Repos used:
@@ -12,7 +12,8 @@ I found these website useful while working on this.
* https://github.com/envoyproxy/envoy/blob/master/api/API_OVERVIEW.md * https://github.com/envoyproxy/envoy/blob/master/api/API_OVERVIEW.md
* https://github.com/envoyproxy/learnenvoy/blob/master/_articles/service-discovery.md * https://github.com/envoyproxy/learnenvoy/blob/master/_articles/service-discovery.md
* This was *really* helpful: https://www.envoyproxy.io/docs/envoy/v1.11.2/api-docs/xds_protocol * This was *really* helpful: https://www.envoyproxy.io/docs/envoy/v1.11.2/api-docs/xds_protocol to
show the flow of the protocol.
# Testing # Testing
@@ -42,16 +43,8 @@ example.org {
Start CoreDNS (`coredns -conf Corefile -dns.port=1053`), and see logging/debugging flow by; the Start CoreDNS (`coredns -conf Corefile -dns.port=1053`), and see logging/debugging flow by; the
test binary should also spew out a bunch of things. CoreDNS willl build up a list of cluster and test binary should also spew out a bunch of things. CoreDNS willl build up a list of cluster and
endpoints. Next you can query it: endpoints. Next you can query it. Note none of the endpoints are HEALTHY so you'll mostly get NODATA
responses, instead of actual records.
~~~ sh
% dig @localhost -p 1053 cluster-v0-0.example.org A
;; QUESTION SECTION:
;cluster-v0-0.example.org. IN A
;; ANSWER SECTION:
cluster-v0-0.example.org. 5 IN A 127.0.0.1
~~~
Note: the xds/test binary is a go-control-plane binary with added debugging that I'm using for Note: the xds/test binary is a go-control-plane binary with added debugging that I'm using for
testing. testing.

View File

@@ -20,10 +20,13 @@ be upgraded, so all traffic to it is drained. Or the entire Kubernetes needs to
endpoints need to be drained from it. endpoints need to be drained from it.
*Traffic* discovers the endpoints via Envoy's xDS protocol. Endpoints and clusters are discovered *Traffic* discovers the endpoints via Envoy's xDS protocol. Endpoints and clusters are discovered
every 10 seconds. The plugin hands out responses that adhere to these assignments. Each DNS response every 10 seconds. The plugin hands out responses that adhere to these assignments. Only endpoints
contains a single IP address that's considered the best one. *Traffic* will load balance A and AAAA that are *healthy* are handed out.
queries. The TTL on these answer is set to 5s. It will only return successful responses either with
an answer or otherwise a NODATA response. Queries for non-existent clusters get a NXDOMAIN. Each DNS response contains a single IP address that's considered the best one. *Traffic* will load
balance A and AAAA queries. The TTL on these answer is set to 5s. It will only return successful
responses either with an answer or otherwise a NODATA response. Queries for non-existent clusters
get a NXDOMAIN.
The *traffic* plugin has no notion of draining, drop overload and anything that advanced, *it just The *traffic* plugin has no notion of draining, drop overload and anything that advanced, *it just
acts upon assignments*. This is means that if a endpoint goes down and *traffic* has not seen a new acts upon assignments*. This is means that if a endpoint goes down and *traffic* has not seen a new
@@ -74,12 +77,11 @@ and "cluster-v0" is one of the load balanced cluster, *traffic* will respond to
## Metrics ## Metrics
What metrics should we do? What metrics should we do? If any? Number of clusters? Number of endpoints and health?
## Ready ## Ready
Should this plugin implement readyness? Should this plugin implement readiness?
## Examples ## Examples
@@ -108,8 +110,7 @@ The following documents provide some background on Envoy's control plane.
## Bugs ## Bugs
Priority from ClusterLoadAssignments is not used. Locality is also not used. Health status of the Priority and locality information from ClusterLoadAssignments is not used.
endpoints is ignore (for now).
Load reporting via xDS is not supported; this can be implemented, but there are some things that Load reporting via xDS is not supported; this can be implemented, but there are some things that
make this difficult. A single (DNS) query is done by a resolver. Behind this resolver there may be make this difficult. A single (DNS) query is done by a resolver. Behind this resolver there may be
@@ -121,9 +122,8 @@ Multiple **TO** addresses is not implemented.
## TODO ## TODO
* acking responses
* correctly tracking versions and pruning old clusters.
* metrics? * metrics?
* how to exactly deal with health status from the endpoints. * more and better testing
* testing
* credentials (other than TLS) - how/what? * credentials (other than TLS) - how/what?
* is the protocol correctly implemented? Should we not have a 10s tick, but wait for responses from
the control plane?

View File

@@ -20,12 +20,19 @@ func TestParseTraffic(t *testing.T) {
input string input string
shouldErr bool shouldErr bool
}{ }{
// ok
{`traffic grpc://127.0.0.1:18000 {
id test-id
}`, false},
// fail // fail
{`traffic`, true},
{`traffic tls://1.1.1.1`, true},
{`traffic { {`traffic {
id bla bla id bla bla
}`, true}, }`, true},
{`traffic { {`traffic {
node bla bla node
}`, true}, }`, true},
} }
for i, test := range tests { for i, test := range tests {

View File

@@ -28,17 +28,12 @@ func (t *Traffic) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
cluster := "" cluster := ""
for _, o := range t.origins { for _, o := range t.origins {
println(o, state.Name())
if strings.HasSuffix(state.Name(), o) { if strings.HasSuffix(state.Name(), o) {
cluster, _ = dnsutil.TrimZone(state.Name(), o) cluster, _ = dnsutil.TrimZone(state.Name(), o)
state.Zone = o state.Zone = o
break break
} }
} }
if cluster == "" {
return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
}
m := new(dns.Msg) m := new(dns.Msg)
m.SetReply(r) m.SetReply(r)
m.Authoritative = true m.Authoritative = true

View File

@@ -1,123 +1,144 @@
package traffic package traffic
/* import (
func TestTraffic(t *testing.T) { "context"
rm := Traffic{Next: handler()} "testing"
"github.com/coredns/coredns/plugin/pkg/dnstest"
"github.com/coredns/coredns/plugin/pkg/dnsutil"
"github.com/coredns/coredns/plugin/test"
"github.com/coredns/coredns/plugin/traffic/xds"
xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
endpointpb "github.com/envoyproxy/go-control-plane/envoy/api/v2/endpoint"
"github.com/miekg/dns"
"google.golang.org/grpc"
)
func TestTraffic(t *testing.T) {
c, err := xds.New("127.0.0.1:0", "test-id", grpc.WithInsecure())
if err != nil {
t.Fatal(err)
}
tr := &Traffic{c: c, origins: []string{"lb.example.org."}}
// the first X records must be cnames after this test
tests := []struct { tests := []struct {
answer []dns.RR cla *xdspb.ClusterLoadAssignment
extra []dns.RR cluster string
cnameAnswer int qtype uint16
cnameExtra int rcode int
addressAnswer int answer string // address value of the A/AAAA record.
addressExtra int ns bool // should there be a ns section.
mxAnswer int
mxExtra int
}{ }{
{ {
answer: []dns.RR{ cla: &xdspb.ClusterLoadAssignment{},
test.CNAME("cname1.region2.skydns.test. 300 IN CNAME cname2.region2.skydns.test."), cluster: "web", qtype: dns.TypeA, rcode: dns.RcodeSuccess, ns: true,
test.CNAME("cname2.region2.skydns.test. 300 IN CNAME cname3.region2.skydns.test."),
test.CNAME("cname5.region2.skydns.test. 300 IN CNAME cname6.region2.skydns.test."),
test.CNAME("cname6.region2.skydns.test. 300 IN CNAME endpoint.region2.skydns.test."),
test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.1"),
test.MX("mx.region2.skydns.test. 300 IN MX 1 mx1.region2.skydns.test."),
test.MX("mx.region2.skydns.test. 300 IN MX 2 mx2.region2.skydns.test."),
test.MX("mx.region2.skydns.test. 300 IN MX 3 mx3.region2.skydns.test."),
},
cnameAnswer: 4,
addressAnswer: 1,
mxAnswer: 3,
}, },
{ {
answer: []dns.RR{ cla: &xdspb.ClusterLoadAssignment{},
test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.1"), cluster: "web", qtype: dns.TypeSRV, rcode: dns.RcodeSuccess, ns: true,
test.MX("mx.region2.skydns.test. 300 IN MX 1 mx1.region2.skydns.test."),
test.CNAME("cname.region2.skydns.test. 300 IN CNAME endpoint.region2.skydns.test."),
},
cnameAnswer: 1,
addressAnswer: 1,
mxAnswer: 1,
}, },
{ {
answer: []dns.RR{ cla: &xdspb.ClusterLoadAssignment{},
test.MX("mx.region2.skydns.test. 300 IN MX 1 mx1.region2.skydns.test."), cluster: "does-not-exist", qtype: dns.TypeA, rcode: dns.RcodeNameError, ns: true,
test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.1"), },
test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.2"), // healthy backend
test.MX("mx.region2.skydns.test. 300 IN MX 1 mx2.region2.skydns.test."), {
test.CNAME("cname2.region2.skydns.test. 300 IN CNAME cname3.region2.skydns.test."), cla: &xdspb.ClusterLoadAssignment{
test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.3"), ClusterName: "web",
test.MX("mx.region2.skydns.test. 300 IN MX 1 mx3.region2.skydns.test."), Endpoints: endpoints([]EndpointHealth{{"127.0.0.1", corepb.HealthStatus_HEALTHY}}),
}, },
extra: []dns.RR{ cluster: "web", qtype: dns.TypeA, rcode: dns.RcodeSuccess, answer: "127.0.0.1",
test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.1"), },
test.AAAA("endpoint.region2.skydns.test. 300 IN AAAA ::1"), // unknown backend
test.MX("mx.region2.skydns.test. 300 IN MX 1 mx1.region2.skydns.test."), {
test.CNAME("cname2.region2.skydns.test. 300 IN CNAME cname3.region2.skydns.test."), cla: &xdspb.ClusterLoadAssignment{
test.MX("mx.region2.skydns.test. 300 IN MX 1 mx2.region2.skydns.test."), ClusterName: "web",
test.A("endpoint.region2.skydns.test. 300 IN A 10.240.0.3"), Endpoints: endpoints([]EndpointHealth{{"127.0.0.1", corepb.HealthStatus_UNKNOWN}}),
test.AAAA("endpoint.region2.skydns.test. 300 IN AAAA ::2"),
test.MX("mx.region2.skydns.test. 300 IN MX 1 mx3.region2.skydns.test."),
}, },
cnameAnswer: 1, cluster: "web", qtype: dns.TypeA, rcode: dns.RcodeSuccess, ns: true,
cnameExtra: 1, },
addressAnswer: 3, // unknown backend and healthy backend
addressExtra: 4, {
mxAnswer: 3, cla: &xdspb.ClusterLoadAssignment{
mxExtra: 3, ClusterName: "web",
Endpoints: endpoints([]EndpointHealth{
{"127.0.0.1", corepb.HealthStatus_UNKNOWN},
{"127.0.0.2", corepb.HealthStatus_HEALTHY},
}),
},
cluster: "web", qtype: dns.TypeA, rcode: dns.RcodeSuccess, answer: "127.0.0.2",
}, },
} }
rec := dnstest.NewRecorder(&test.ResponseWriter{}) ctx := context.TODO()
for i, test := range tests { for i, tc := range tests {
req := new(dns.Msg) a := xds.NewAssignment()
req.SetQuestion("region2.skydns.test.", dns.TypeSRV) a.SetClusterLoadAssignment("web", tc.cla) // web is our cluster
req.Answer = test.answer c.SetAssignments(a)
req.Extra = test.extra
_, err := rm.ServeDNS(context.TODO(), rec, req) m := new(dns.Msg)
cl := dnsutil.Join(tc.cluster, tr.origins[0])
m.SetQuestion(cl, tc.qtype)
rec := dnstest.NewRecorder(&test.ResponseWriter{})
_, err := tr.ServeDNS(ctx, rec, m)
if err != nil { if err != nil {
t.Errorf("Test %d: Expected no error, but got %s", i, err) t.Errorf("Test %d: Expected no error, but got %q", i, err)
continue }
if rec.Msg.Rcode != tc.rcode {
t.Errorf("Test %d: Expected no rcode %d, but got %d", i, tc.rcode, rec.Msg.Rcode)
}
if tc.ns && len(rec.Msg.Ns) == 0 {
t.Errorf("Test %d: Expected authority section, but got none", i)
}
if tc.answer != "" && len(rec.Msg.Answer) == 0 {
t.Fatalf("Test %d: Expected answer section, but got none", i)
}
if tc.answer != "" {
record := rec.Msg.Answer[0]
addr := ""
switch x := record.(type) {
case *dns.A:
addr = x.A.String()
case *dns.AAAA:
addr = x.AAAA.String()
}
if tc.answer != addr {
t.Errorf("Test %d: Expected answer %s, but got %s", i, tc.answer, addr)
}
} }
cname, address, mx, sorted := countRecords(rec.Msg.Answer)
if !sorted {
t.Errorf("Test %d: Expected CNAMEs, then AAAAs, then MX in Answer, but got mixed", i)
}
if cname != test.cnameAnswer {
t.Errorf("Test %d: Expected %d CNAMEs in Answer, but got %d", i, test.cnameAnswer, cname)
}
if address != test.addressAnswer {
t.Errorf("Test %d: Expected %d A/AAAAs in Answer, but got %d", i, test.addressAnswer, address)
}
if mx != test.mxAnswer {
t.Errorf("Test %d: Expected %d MXs in Answer, but got %d", i, test.mxAnswer, mx)
}
cname, address, mx, sorted = countRecords(rec.Msg.Extra)
if !sorted {
t.Errorf("Test %d: Expected CNAMEs, then AAAAs, then MX in Extra, but got mixed", i)
}
if cname != test.cnameExtra {
t.Errorf("Test %d: Expected %d CNAMEs in Extra, but got %d", i, test.cnameAnswer, cname)
}
if address != test.addressExtra {
t.Errorf("Test %d: Expected %d A/AAAAs in Extra, but got %d", i, test.addressAnswer, address)
}
if mx != test.mxExtra {
t.Errorf("Test %d: Expected %d MXs in Extra, but got %d", i, test.mxAnswer, mx)
}
} }
} }
func handler() plugin.Handler { type EndpointHealth struct {
return plugin.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { Address string
w.WriteMsg(r) Health corepb.HealthStatus
return dns.RcodeSuccess, nil }
})
func endpoints(e []EndpointHealth) []*endpointpb.LocalityLbEndpoints {
ep := make([]*endpointpb.LocalityLbEndpoints, len(e))
for i := range e {
ep[i] = &endpointpb.LocalityLbEndpoints{
LbEndpoints: []*endpointpb.LbEndpoint{{
HostIdentifier: &endpointpb.LbEndpoint_Endpoint{
Endpoint: &endpointpb.Endpoint{
Address: &corepb.Address{
Address: &corepb.Address_SocketAddress{
SocketAddress: &corepb.SocketAddress{
Address: e[i].Address,
},
},
},
},
},
HealthStatus: e[i].Health,
}},
}
}
return ep
} }
*/

View File

@@ -6,15 +6,21 @@ import (
"sync" "sync"
xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2" xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2"
corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core"
) )
type assignment struct { type assignment struct {
mu sync.RWMutex mu sync.RWMutex
cla map[string]*xdspb.ClusterLoadAssignment cla map[string]*xdspb.ClusterLoadAssignment
version int // not sure what do with and if we should discard all clusters.
} }
func (a *assignment) setClusterLoadAssignment(cluster string, cla *xdspb.ClusterLoadAssignment) { // NewAssignment returns a pointer to an assignment.
func NewAssignment() *assignment {
return &assignment{cla: make(map[string]*xdspb.ClusterLoadAssignment)}
}
// SetClusterLoadAssignment sets the assignment for the cluster to cla.
func (a *assignment) SetClusterLoadAssignment(cluster string, cla *xdspb.ClusterLoadAssignment) {
// If cla is nil we just found a cluster, check if we already know about it, or if we need to make a new entry. // If cla is nil we just found a cluster, check if we already know about it, or if we need to make a new entry.
a.mu.Lock() a.mu.Lock()
defer a.mu.Unlock() defer a.mu.Unlock()
@@ -30,7 +36,8 @@ func (a *assignment) setClusterLoadAssignment(cluster string, cla *xdspb.Cluster
} }
func (a *assignment) clusterLoadAssignment(cluster string) *xdspb.ClusterLoadAssignment { // ClusterLoadAssignment returns the assignment for the cluster or nil if there is none.
func (a *assignment) ClusterLoadAssignment(cluster string) *xdspb.ClusterLoadAssignment {
a.mu.RLock() a.mu.RLock()
cla, ok := a.cla[cluster] cla, ok := a.cla[cluster]
a.mu.RUnlock() a.mu.RUnlock()
@@ -52,55 +59,58 @@ func (a *assignment) clusters() []string {
return clusters return clusters
} }
// Select selects a backend from cla, using weighted random selection. It only selects // Select selects a backend from cluster load assignments, using weighted random selection. It only selects
// backends that are reporting healthy. // backends that are reporting healthy.
func (a *assignment) Select(cluster string) (net.IP, bool) { func (a *assignment) Select(cluster string) (net.IP, bool) {
cla := a.clusterLoadAssignment(cluster) cla := a.ClusterLoadAssignment(cluster)
if cla == nil { if cla == nil {
return nil, false return nil, false
} }
total := 0 total := 0
i := 0 healthy := 0
for _, ep := range cla.Endpoints { for _, ep := range cla.Endpoints {
for _, lb := range ep.GetLbEndpoints() { for _, lb := range ep.GetLbEndpoints() {
// if lb.GetHealthStatus() != corepb.HealthStatus_HEALTHY { if lb.GetHealthStatus() != corepb.HealthStatus_HEALTHY {
// continue continue
// } }
total += int(lb.GetLoadBalancingWeight().GetValue()) total += int(lb.GetLoadBalancingWeight().GetValue())
i++ healthy++
} }
} }
if healthy == 0 {
return nil, true
}
if total == 0 { if total == 0 {
// all weights are 0, randomly select one of the endpoints. // all weights are 0, randomly select one of the endpoints.
r := rand.Intn(i) r := rand.Intn(healthy)
i := 0 i := 0
for _, ep := range cla.Endpoints { for _, ep := range cla.Endpoints {
for _, lb := range ep.GetLbEndpoints() { for _, lb := range ep.GetLbEndpoints() {
// if lb.GetHealthStatus() != corepb.HealthStatus_HEALTHY { if lb.GetHealthStatus() != corepb.HealthStatus_HEALTHY {
// continue continue
// } }
if r == i { if r == i {
return net.ParseIP(lb.GetEndpoint().GetAddress().GetSocketAddress().GetAddress()), true return net.ParseIP(lb.GetEndpoint().GetAddress().GetSocketAddress().GetAddress()), true
} }
i++ i++
} }
} }
return nil return nil, true
} }
r := rand.Intn(total) + 1 r := rand.Intn(total) + 1
for _, ep := range cla.Endpoints { for _, ep := range cla.Endpoints {
for _, lb := range ep.GetLbEndpoints() { for _, lb := range ep.GetLbEndpoints() {
// if lb.GetHealthStatus() != corepb.HealthStatus_HEALTHY { if lb.GetHealthStatus() != corepb.HealthStatus_HEALTHY {
// continue continue
// } }
r -= int(lb.GetLoadBalancingWeight().GetValue()) r -= int(lb.GetLoadBalancingWeight().GetValue())
if r <= 0 { if r <= 0 {
return net.ParseIP(lb.GetEndpoint().GetAddress().GetSocketAddress().GetAddress()), true return net.ParseIP(lb.GetEndpoint().GetAddress().GetSocketAddress().GetAddress()), true
} }
} }
} }
return nil, false return nil, true
} }

View File

@@ -52,12 +52,14 @@ type adsStream adsgrpc.AggregatedDiscoveryService_StreamAggregatedResourcesClien
type Client struct { type Client struct {
cc *grpc.ClientConn cc *grpc.ClientConn
ctx context.Context ctx context.Context
assignments *assignment assignments *assignment // assignments contains the current clusters and endpoints.
node *corepb.Node node *corepb.Node
cancel context.CancelFunc cancel context.CancelFunc
stop chan struct{} stop chan struct{}
mu sync.RWMutex mu sync.RWMutex
nonce string
version map[string]string
nonce map[string]string
} }
// New returns a new client that's dialed to addr using node as the local identifier. // New returns a new client that's dialed to addr using node as the local identifier.
@@ -79,6 +81,7 @@ func New(addr, node string, opts ...grpc.DialOption) (*Client, error) {
}, },
} }
c.assignments = &assignment{cla: make(map[string]*xdspb.ClusterLoadAssignment)} c.assignments = &assignment{cla: make(map[string]*xdspb.ClusterLoadAssignment)}
c.version, c.nonce = make(map[string]string), make(map[string]string)
c.ctx, c.cancel = context.WithCancel(context.Background()) c.ctx, c.cancel = context.WithCancel(context.Background())
return c, nil return c, nil
@@ -106,13 +109,15 @@ func (c *Client) Run() {
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
tick := time.NewTicker(1 * time.Second) if err := c.clusterDiscovery(stream, c.Version(cdsURL), c.Nonce(cdsURL), []string{}); err != nil {
log.Debug(err)
}
tick := time.NewTicker(10 * time.Second)
for { for {
select { select {
case <-tick.C: case <-tick.C:
// send empty list for cluster discovery again and again // send empty list for cluster discovery every 10 seconds
log.Debugf("Requesting cluster list, nonce %q:", c.Nonce()) if err := c.clusterDiscovery(stream, c.Version(cdsURL), c.Nonce(cdsURL), []string{}); err != nil {
if err := c.clusterDiscovery(stream, "", c.Nonce(), []string{}); err != nil {
log.Debug(err) log.Debug(err)
} }
@@ -124,7 +129,7 @@ func (c *Client) Run() {
}() }()
if err := c.Receive(stream); err != nil { if err := c.Receive(stream); err != nil {
log.Debug(err) log.Warning(err)
} }
close(done) close(done)
} }
@@ -164,7 +169,7 @@ func (c *Client) Receive(stream adsStream) error {
switch resp.GetTypeUrl() { switch resp.GetTypeUrl() {
case cdsURL: case cdsURL:
a := &assignment{cla: make(map[string]*xdspb.ClusterLoadAssignment)} a := NewAssignment()
for _, r := range resp.GetResources() { for _, r := range resp.GetResources() {
var any ptypes.DynamicAny var any ptypes.DynamicAny
if err := ptypes.UnmarshalAny(r, &any); err != nil { if err := ptypes.UnmarshalAny(r, &any); err != nil {
@@ -175,24 +180,18 @@ func (c *Client) Receive(stream adsStream) error {
if !ok { if !ok {
continue continue
} }
a.setClusterLoadAssignment(cluster.GetName(), nil) a.SetClusterLoadAssignment(cluster.GetName(), nil)
} }
log.Debugf("Cluster discovery processed with %d resources", len(resp.GetResources())) log.Debugf("Cluster discovery processed with %d resources, version %q and nonce %q, clusters: %v", len(resp.GetResources()), c.Version(cdsURL), c.Nonce(cdsURL), a.clusters())
// set our local administration and ack the reply. Empty version would signal NACK.
// ack the CDS proto, with we we've got. (empty version would be NACK) c.SetNonce(cdsURL, resp.GetNonce())
if err := c.clusterDiscovery(stream, resp.GetVersionInfo(), resp.GetNonce(), a.clusters()); err != nil { c.SetVersion(cdsURL, resp.GetVersionInfo())
log.Debug(err)
continue
}
// need to figure out how to handle the versions and nounces exactly.
c.SetNonce(resp.GetNonce())
c.SetAssignments(a) c.SetAssignments(a)
c.clusterDiscovery(stream, resp.GetVersionInfo(), resp.GetNonce(), a.clusters())
// now kick off discovery for endpoints // now kick off discovery for endpoints
if err := c.endpointDiscovery(stream, "", resp.GetNonce(), a.clusters()); err != nil { if err := c.endpointDiscovery(stream, c.Version(edsURL), c.Nonce(edsURL), a.clusters()); err != nil {
log.Debug(err) log.Debug(err)
continue
} }
case edsURL: case edsURL:
for _, r := range resp.GetResources() { for _, r := range resp.GetResources() {
@@ -205,10 +204,12 @@ func (c *Client) Receive(stream adsStream) error {
if !ok { if !ok {
continue continue
} }
c.assignments.setClusterLoadAssignment(cla.GetClusterName(), cla) c.assignments.SetClusterLoadAssignment(cla.GetClusterName(), cla)
// ack the bloody thing
} }
log.Debugf("Endpoint discovery processed with %d resources", len(resp.GetResources())) log.Debugf("Endpoint discovery processed with %d resources, version %q and nonce %q, clusters: %v", len(resp.GetResources()), c.Version(edsURL), c.Nonce(edsURL), c.assignments.clusters())
// set our local administration and ack the reply. Empty version would signal NACK.
c.SetNonce(edsURL, resp.GetNonce())
c.SetVersion(edsURL, resp.GetVersionInfo())
default: default:
return fmt.Errorf("unknown response URL for discovery: %q", resp.GetTypeUrl()) return fmt.Errorf("unknown response URL for discovery: %q", resp.GetTypeUrl())
@@ -218,4 +219,9 @@ func (c *Client) Receive(stream adsStream) error {
// Select returns an address that is deemed to be the correct one for this cluster. The returned // Select returns an address that is deemed to be the correct one for this cluster. The returned
// boolean indicates if the cluster exists. // boolean indicates if the cluster exists.
func (c *Client) Select(cluster string) (net.IP, bool) { return c.assignments.Select(cluster) } func (c *Client) Select(cluster string) (net.IP, bool) {
if cluster == "" {
return nil, false
}
return c.assignments.Select(cluster)
}

View File

@@ -1,17 +1,5 @@
package xds package xds
func (c *Client) Nonce() string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.nonce
}
func (c *Client) SetNonce(n string) {
c.mu.Lock()
defer c.mu.Unlock()
c.nonce = n
}
func (c *Client) Assignments() *assignment { func (c *Client) Assignments() *assignment {
c.mu.RLock() c.mu.RLock()
defer c.mu.RUnlock() defer c.mu.RUnlock()
@@ -23,3 +11,27 @@ func (c *Client) SetAssignments(a *assignment) {
defer c.mu.Unlock() defer c.mu.Unlock()
c.assignments = a c.assignments = a
} }
func (c *Client) Version(typeURL string) string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.version[typeURL]
}
func (c *Client) SetVersion(typeURL, a string) {
c.mu.Lock()
defer c.mu.Unlock()
c.version[typeURL] = a
}
func (c *Client) Nonce(typeURL string) string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.nonce[typeURL]
}
func (c *Client) SetNonce(typeURL, n string) {
c.mu.Lock()
defer c.mu.Unlock()
c.nonce[typeURL] = n
}