mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
fix: upgrade coredns to v1.10.1 at 4.0 (#24619)
Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
@@ -17,7 +17,11 @@ package main
|
||||
import (
|
||||
"github.com/coredns/coredns/core/dnsserver"
|
||||
"github.com/coredns/coredns/coremain"
|
||||
_ "github.com/coredns/coredns/plugin/acl"
|
||||
_ "github.com/coredns/coredns/plugin/bind"
|
||||
_ "github.com/coredns/coredns/plugin/bufsize"
|
||||
_ "github.com/coredns/coredns/plugin/cache"
|
||||
_ "github.com/coredns/coredns/plugin/cancel"
|
||||
_ "github.com/coredns/coredns/plugin/chaos"
|
||||
_ "github.com/coredns/coredns/plugin/debug"
|
||||
_ "github.com/coredns/coredns/plugin/errors"
|
||||
@@ -25,12 +29,18 @@ import (
|
||||
_ "github.com/coredns/coredns/plugin/forward"
|
||||
_ "github.com/coredns/coredns/plugin/health"
|
||||
_ "github.com/coredns/coredns/plugin/hosts"
|
||||
_ "github.com/coredns/coredns/plugin/loadbalance"
|
||||
_ "github.com/coredns/coredns/plugin/local"
|
||||
_ "github.com/coredns/coredns/plugin/log"
|
||||
_ "github.com/coredns/coredns/plugin/metrics"
|
||||
_ "github.com/coredns/coredns/plugin/loop"
|
||||
_ "github.com/coredns/coredns/plugin/metrics" // prometheus
|
||||
_ "github.com/coredns/coredns/plugin/nsid"
|
||||
_ "github.com/coredns/coredns/plugin/proxy"
|
||||
_ "github.com/coredns/coredns/plugin/ready"
|
||||
_ "github.com/coredns/coredns/plugin/reload"
|
||||
_ "github.com/coredns/coredns/plugin/rewrite"
|
||||
_ "github.com/coredns/coredns/plugin/timeouts"
|
||||
_ "github.com/coredns/coredns/plugin/trace"
|
||||
_ "github.com/coredns/coredns/plugin/whoami"
|
||||
_ "github.com/mholt/caddy/startupshutdown"
|
||||
|
||||
_ "yunion.io/x/onecloud/pkg/dns"
|
||||
@@ -38,18 +48,31 @@ import (
|
||||
)
|
||||
|
||||
var directives = []string{
|
||||
"cancel",
|
||||
"timeouts",
|
||||
"reload",
|
||||
"nsid",
|
||||
"bufsize",
|
||||
"bind",
|
||||
"debug",
|
||||
"trace",
|
||||
"ready",
|
||||
"health",
|
||||
"prometheus",
|
||||
"errors",
|
||||
"log",
|
||||
"local",
|
||||
"chaos",
|
||||
"loadbalance",
|
||||
"cache",
|
||||
"rewrite",
|
||||
"acl",
|
||||
"yunion",
|
||||
"hosts",
|
||||
"file",
|
||||
"yunion",
|
||||
"loop",
|
||||
"forward",
|
||||
"proxy",
|
||||
"whoami",
|
||||
"startup",
|
||||
"shutdown",
|
||||
}
|
||||
|
||||
79
go.mod
79
go.mod
@@ -15,7 +15,8 @@ require (
|
||||
github.com/cheggaaa/pb/v3 v3.0.8
|
||||
github.com/containerd/containerd v1.7.13
|
||||
github.com/containerd/typeurl v1.0.2
|
||||
github.com/coredns/coredns v1.3.0
|
||||
github.com/coredns/caddy v1.1.1
|
||||
github.com/coredns/coredns v1.10.1
|
||||
github.com/coreos/go-iptables v0.6.0
|
||||
github.com/creack/pty v1.1.18
|
||||
github.com/docker/go-units v0.5.0
|
||||
@@ -53,14 +54,14 @@ require (
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7
|
||||
github.com/mdlayher/packet v1.1.2
|
||||
github.com/mholt/caddy v0.10.11
|
||||
github.com/miekg/dns v1.1.25
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/minio/minio-go v6.0.14+incompatible
|
||||
github.com/mitchellh/go-wordwrap v1.0.1
|
||||
github.com/moby/term v0.5.0
|
||||
github.com/mogaika/udf v0.0.0-20171019171931-167f0ab01c73
|
||||
github.com/opencontainers/runtime-spec v1.1.0
|
||||
github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490
|
||||
github.com/pierrec/lz4/v4 v4.1.15
|
||||
github.com/pierrec/lz4/v4 v4.1.17
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/pquerna/otp v1.2.0
|
||||
@@ -83,8 +84,8 @@ require (
|
||||
github.com/xuri/excelize/v2 v2.7.1
|
||||
github.com/zeebo/xxh3 v1.0.2
|
||||
github.com/zexi/influxql-to-metricsql v0.1.1
|
||||
go.etcd.io/etcd/api/v3 v3.5.0
|
||||
go.etcd.io/etcd/client/v3 v3.5.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.7
|
||||
go.etcd.io/etcd/client/v3 v3.5.7
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sync v0.16.0
|
||||
@@ -128,19 +129,23 @@ require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.9.6 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.1.0 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4 // indirect
|
||||
github.com/DataDog/dd-trace-go v0.6.1 // indirect
|
||||
github.com/DataDog/zstd v1.3.4 // indirect
|
||||
github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583 // indirect
|
||||
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.42.0-rc.1 // indirect
|
||||
github.com/DataDog/datadog-go v4.8.2+incompatible // indirect
|
||||
github.com/DataDog/datadog-go/v5 v5.0.2 // indirect
|
||||
github.com/DataDog/go-tuf v0.3.0--fix-localmeta-fork // indirect
|
||||
github.com/DataDog/sketches-go v1.2.1 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.4 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.2.3 // indirect
|
||||
github.com/Shopify/sarama v1.20.0 // indirect
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/VividCortex/ewma v1.1.1 // indirect
|
||||
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect
|
||||
@@ -162,8 +167,8 @@ require (
|
||||
github.com/anacrolix/upnp v0.1.4 // indirect
|
||||
github.com/anacrolix/utp v0.1.0 // indirect
|
||||
github.com/aokoli/goutils v1.0.1 // indirect
|
||||
github.com/apache/thrift v0.13.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.39.0 // indirect
|
||||
github.com/apparentlymart/go-cidr v1.1.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.194 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.12 // indirect
|
||||
@@ -210,18 +215,15 @@ require (
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/dimchansky/utfbom v1.1.0 // indirect
|
||||
github.com/dnstap/golang-dnstap v0.0.0-20170829151710-2cf77a2b5e11 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/dnstap/golang-dnstap v0.4.0 // indirect
|
||||
github.com/docker/go-events v0.0.0-20250114142523-c867878c5e32 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/eapache/go-resiliency v1.1.0 // indirect
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
|
||||
github.com/eapache/queue v1.1.0 // indirect
|
||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
|
||||
github.com/euank/go-kmsg-parser v2.0.0+incompatible // indirect
|
||||
github.com/farsightsec/golang-framestream v0.0.0-20181102145529-8a0cb8ba8710 // indirect
|
||||
github.com/farsightsec/golang-framestream v0.3.0 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
|
||||
@@ -229,7 +231,6 @@ require (
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 // indirect
|
||||
github.com/go-llsqlite/crawshaw v0.5.2-0.20240425034140-f30eb7704568 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
@@ -244,6 +245,8 @@ require (
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/gofrs/uuid v4.1.0+incompatible // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/glog v1.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
@@ -262,7 +265,8 @@ require (
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go v1.0.26 // indirect
|
||||
github.com/icholy/digest v1.1.0 // indirect
|
||||
github.com/imdario/mergo v0.3.6 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||
github.com/jdcloud-api/jdcloud-sdk-go v1.55.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
@@ -286,7 +290,7 @@ require (
|
||||
github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect
|
||||
github.com/ma314smith/signedxml v0.0.0-20210628192057-abc5b481ae1c // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f // indirect
|
||||
@@ -321,9 +325,11 @@ require (
|
||||
github.com/opencontainers/runc v1.1.5 // indirect
|
||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
|
||||
github.com/opentracing/opentracing-go v1.0.2 // indirect
|
||||
github.com/openzipkin/zipkin-go-opentracing v0.3.4 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.1 // indirect
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/pion/datachannel v1.5.2 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.4 // indirect
|
||||
@@ -348,16 +354,16 @@ require (
|
||||
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
|
||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/common v0.39.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/protolambda/ctxlock v0.1.0 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.3 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
|
||||
github.com/smartystreets/assertions v1.2.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
@@ -368,6 +374,7 @@ require (
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.24 // indirect
|
||||
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e // indirect
|
||||
github.com/tidwall/btree v1.6.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.5.0 // indirect
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
@@ -381,7 +388,7 @@ require (
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect
|
||||
@@ -391,16 +398,22 @@ require (
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.17.0 // indirect
|
||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/mod v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.17.0 // indirect
|
||||
golang.org/x/term v0.34.0 // indirect
|
||||
golang.org/x/tools v0.35.0 // indirect
|
||||
google.golang.org/api v0.167.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240304161311-37d4d3c04a78 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.47.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
inet.af/netaddr v0.0.0-20220617031823-097006376321 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
|
||||
lukechampine.com/blake3 v1.1.6 // indirect
|
||||
@@ -415,3 +428,5 @@ require (
|
||||
)
|
||||
|
||||
replace github.com/influxdata/promql/v2 => github.com/zexi/promql/v2 v2.12.1
|
||||
|
||||
replace inet.af/netaddr => github.com/inetaf/netaddr v0.0.0-20211027220019-c74959edd3b6
|
||||
|
||||
203
go.sum
203
go.sum
@@ -58,41 +58,47 @@ github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
||||
github.com/Azure/go-autorest/autorest v0.9.6 h1:5YWtOnckcudzIw8lPPBcWOnmIFWMtHci1ZWAZulMSx0=
|
||||
github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
|
||||
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
|
||||
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
|
||||
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/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
|
||||
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
|
||||
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||
github.com/DataDog/dd-trace-go v0.6.1 h1:nsZ2lohbSw1CKtfNRu3wPh1jFirv6XSz8vqNpuIYWbM=
|
||||
github.com/DataDog/dd-trace-go v0.6.1/go.mod h1:SmQTTcC37XMyEm75HV0AWiZIYxDiaNhRi49zorIpW+o=
|
||||
github.com/DataDog/zstd v1.3.4 h1:LAGHkXuvC6yky+C2CUG2tD7w8QlrUwpue8XwIh0X4AY=
|
||||
github.com/DataDog/zstd v1.3.4/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583 h1:3nVO1nQyh64IUY6BPZUpMYMZ738Pu+LsMt3E0eqqIYw=
|
||||
github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583/go.mod h1:EP9f4GqaDJyP1F5jTNMtzdIpw3JpNs3rMSJOnYywCiw=
|
||||
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.42.0-rc.1 h1:Rmz52Xlc5k3WzAHzD0SCH4USCzyti7EbK4HtrHys3ME=
|
||||
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.42.0-rc.1/go.mod h1:VVMDDibJxYEkwcLdZBT2g8EHKpbMT4JdOhRbQ9GdjbM=
|
||||
github.com/DataDog/datadog-go v4.8.2+incompatible h1:qbcKSx29aBLD+5QLvlQZlGmRMF/FfGqFLFev/1TDzRo=
|
||||
github.com/DataDog/datadog-go v4.8.2+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/DataDog/datadog-go/v5 v5.0.2 h1:UFtEe7662/Qojxkw1d6SboAeA0CPI3naKhVASwFn+04=
|
||||
github.com/DataDog/datadog-go/v5 v5.0.2/go.mod h1:ZI9JFB4ewXbw1sBnF4sxsR2k1H3xjV+PUAOUsHvKpcU=
|
||||
github.com/DataDog/go-tuf v0.3.0--fix-localmeta-fork h1:yBq5PrAtrM4yVeSzQ+bn050+Ysp++RKF1QmtkL4VqvU=
|
||||
github.com/DataDog/go-tuf v0.3.0--fix-localmeta-fork/go.mod h1:yA5JwkZsHTLuqq3zaRgUQf35DfDkpOZqgtBqHKpwrBs=
|
||||
github.com/DataDog/sketches-go v1.2.1 h1:qTBzWLnZ3kM2kw39ymh6rMcnN+5VULwFs++lEYUUsro=
|
||||
github.com/DataDog/sketches-go v1.2.1/go.mod h1:1xYmPLY1So10AwxV6MJV0J53XVH+WL9Ad1KetxVivVI=
|
||||
github.com/LeeEirc/terminalparser v0.0.0-20240205084113-fbf78c8480f2 h1:XGB3B0651J1uKOE1KJa1gsrV/DO1kthhk2NTDUHATgs=
|
||||
github.com/LeeEirc/terminalparser v0.0.0-20240205084113-fbf78c8480f2/go.mod h1:tiLv6VBLH4Z3KdBSe2qIKRwQDGCVQ9/F5fOKpQGvyoA=
|
||||
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
|
||||
@@ -110,9 +116,6 @@ github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06
|
||||
github.com/RoaringBitmap/roaring v1.2.3 h1:yqreLINqIrX22ErkKI0vY47/ivtJr6n+kMhVOVmhWBY=
|
||||
github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/sarama v1.20.0 h1:wAMHhl1lGRlobeoV/xOKpbqD2OQsOvY4A/vIOGroIe8=
|
||||
github.com/Shopify/sarama v1.20.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
@@ -195,12 +198,12 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
|
||||
github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg=
|
||||
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
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/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
|
||||
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
|
||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||
github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
|
||||
github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo=
|
||||
github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.44.194 h1:1ZDK+QDcc5oRbZGgRZSz561eR8XVizXCeGpoZKo33NU=
|
||||
github.com/aws/aws-sdk-go v1.44.194/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
|
||||
@@ -305,6 +308,7 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
|
||||
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||
@@ -324,8 +328,10 @@ github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY
|
||||
github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
|
||||
github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
|
||||
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
|
||||
github.com/coredns/coredns v1.3.0 h1:Ubjr8ZrOpEj95/A9dqQqQAG2k5DWEFdg2ZB3tUXuULA=
|
||||
github.com/coredns/coredns v1.3.0/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0=
|
||||
github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0=
|
||||
github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4=
|
||||
github.com/coredns/coredns v1.10.1 h1:6OyL7tcvYxeNHONj5iQlVM2GXBzAOq57L3/LUKP1DbA=
|
||||
github.com/coredns/coredns v1.10.1/go.mod h1:oGgoY6cRrdJzKgNrsT30Hztu7/MutSHCYwqGDWngXCc=
|
||||
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
|
||||
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
@@ -346,12 +352,14 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnstap/golang-dnstap v0.0.0-20170829151710-2cf77a2b5e11 h1:m8nX8hsUghn853BJ5qB0lX+VvS6LTJPksWyILFZRYN4=
|
||||
github.com/dnstap/golang-dnstap v0.0.0-20170829151710-2cf77a2b5e11/go.mod h1:s1PfVYYVmTMgCSPtho4LKBDecEHJWtiVDPNv78Z985U=
|
||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
||||
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
|
||||
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
||||
github.com/dnstap/golang-dnstap v0.4.0 h1:KRHBoURygdGtBjDI2w4HifJfMAhhOqDuktAokaSa234=
|
||||
github.com/dnstap/golang-dnstap v0.4.0/go.mod h1:FqsSdH58NAmkAvKcpyxht7i4FoBjKu8E4JUPt8ipSUs=
|
||||
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
|
||||
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v20.10.18+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
@@ -370,11 +378,9 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3
|
||||
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
||||
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
||||
@@ -388,7 +394,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
@@ -396,8 +401,8 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZ
|
||||
github.com/euank/go-kmsg-parser v2.0.0+incompatible h1:cHD53+PLQuuQyLZeriD1V/esuG4MuU0Pjs5y6iknohY=
|
||||
github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/farsightsec/golang-framestream v0.0.0-20181102145529-8a0cb8ba8710 h1:QdyRyGZWLEvJG5Kw3VcVJvhXJ5tZ1MkRgqpJOEZSySM=
|
||||
github.com/farsightsec/golang-framestream v0.0.0-20181102145529-8a0cb8ba8710/go.mod h1:eNde4IQyEiA5br02AouhEHCu3p3UzrCdFR4LuQHklMI=
|
||||
github.com/farsightsec/golang-framestream v0.3.0 h1:/spFQHucTle/ZIPkYqrfshQqPe2VQEzesH243TjIwqA=
|
||||
github.com/farsightsec/golang-framestream v0.3.0/go.mod h1:eNde4IQyEiA5br02AouhEHCu3p3UzrCdFR4LuQHklMI=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
@@ -405,6 +410,7 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e h1:P10tZmVD2XclAaT9l7OduMH1OLFzTa1wUuUqHZnEdI0=
|
||||
github.com/fernet/fernet-go v0.0.0-20180830025343-9eac43b88a5e/go.mod h1:2H9hjfbpSMHwY503FclkV/lZTBh2YlOmLLSda12uL8c=
|
||||
github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e/go.mod h1:HyVoz1Mz5Co8TFO8EupIdlcpwShBmY98dkT2xeHkvEI=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
||||
@@ -449,7 +455,6 @@ github.com/go-llsqlite/crawshaw v0.5.2-0.20240425034140-f30eb7704568/go.mod h1:/
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
@@ -504,6 +509,9 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-plus/errors v1.0.0 h1:9Rh4itnph6OYO7wm9u3FUTLMFMKCoL07+bLiYKELxWw=
|
||||
github.com/golang-plus/errors v1.0.0/go.mod h1:YTFZjOTBcUFieeZe4q+7Umfu/sceSr3XQyBIkTkzpd8=
|
||||
github.com/golang-plus/testing v1.0.0 h1:azmFWFV44LHK/j/jAkNTbzB2G2b6HdmqF358r73m8kg=
|
||||
@@ -511,6 +519,8 @@ github.com/golang-plus/testing v1.0.0/go.mod h1:psANDlKPZ0ycedUzCS0Trf8h98sFyOyB
|
||||
github.com/golang-plus/uuid v1.0.0 h1:ga84hG89vda++AGpup4N/cbfbM3O4d3R5ON9/jcN6AY=
|
||||
github.com/golang-plus/uuid v1.0.0/go.mod h1:pBDDRrdgRHHqyYlj1d1i2gwyBq62Zc+sP2cDiCDkBLI=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
|
||||
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
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/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -591,6 +601,7 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
@@ -623,7 +634,6 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gosuri/uitable v0.0.0-20160404203958-36ee7e946282 h1:KFqmdzEPbU7Uck2tn50t+HQXZNVkxe8M9qRb/ZoSHaE=
|
||||
github.com/gosuri/uitable v0.0.0-20160404203958-36ee7e946282/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
|
||||
@@ -648,12 +658,17 @@ github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq
|
||||
github.com/huaweicloud/huaweicloud-sdk-go v1.0.26 h1:aWTl4Ng9lZUW1DupYyqYiqkTwuTKMmqUZBKLmX0iWxo=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go v1.0.26/go.mod h1:YHXxw/bm7AohI4jTY8Z43JYw+DPCDLqjxoLTqmcyja4=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/icholy/digest v1.1.0 h1:HfGg9Irj7i+IX1o1QAmPfIBNu/Q5A5Tu3n/MED9k9H4=
|
||||
github.com/icholy/digest v1.1.0/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y=
|
||||
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.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inetaf/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:E9qjt3p/44HP4XfAABZhSgNZHUufBVKIm/nyPUlVpvY=
|
||||
github.com/inetaf/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8=
|
||||
github.com/influxdata/influxql v1.1.0 h1:sPsaumLFRPMwR5QtD3Up54HXpNND8Eu7G1vQFmi3quQ=
|
||||
github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo=
|
||||
github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 h1:w66aaP3c6SIQ0pi3QH1Tb4AMO3aWoEPxd1CNvLphbkA=
|
||||
github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9/go.mod h1:BaIJzjD2ZnHmx2acPF6XfGLPzNCMiBbMRqJr+8/8uRI=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jaypipes/ghw v0.11.0 h1:i0pKvAM7eZk0KvLm9vzpcpDKTRnfR6AQ5pFkPVnYJXU=
|
||||
@@ -756,8 +771,9 @@ github.com/mark3labs/mcp-go v0.42.0/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9a
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
@@ -794,8 +810,9 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mholt/caddy v0.10.11 h1:s8X+R8DuBbrrMuUTcWSxlDe567B0s5EDmiDBKSYsioY=
|
||||
github.com/mholt/caddy v0.10.11/go.mod h1:Wb1PlT4DAYSqOEd03MsqkdkXnTxA8v9pKjdpxbqM1kY=
|
||||
github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg=
|
||||
github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989 h1:PS1dLCGtD8bb9RPKJrc8bS7qHL6JnW1CZvwzH9dPoUs=
|
||||
github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY=
|
||||
github.com/minio/cli v1.20.0/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY=
|
||||
@@ -867,9 +884,11 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs=
|
||||
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
@@ -878,6 +897,7 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
||||
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.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys=
|
||||
github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
@@ -896,21 +916,25 @@ github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaL
|
||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 h1:uhcF5Jd7rP9DVEL10Siffyepr6SvlKbUsjH5JpNCRi8=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0/go.mod h1:+oCZ5GXXr7KPI/DNOQORPTq5AWHfALJj9c72b0+YsEY=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go-opentracing v0.3.4 h1:x/pBv/5VJNWkcHF1G9xqhug8Iw7X1y1zOMzDmyuvP2g=
|
||||
github.com/openzipkin/zipkin-go-opentracing v0.3.4/go.mod h1:js2AbwmHW0YD9DwIw2JhQWmbfFi/UnWyYwdVhqbCDOE=
|
||||
github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfPcEO5A=
|
||||
github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM=
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU=
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 h1:QTvNkZ5ylY0PGgA+Lih+GdboMLY/G9SEGLMEGVjTVA4=
|
||||
github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
|
||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E=
|
||||
github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ=
|
||||
github.com/pion/dtls/v2 v2.1.3/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus=
|
||||
@@ -995,8 +1019,9 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI=
|
||||
github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
@@ -1010,8 +1035,6 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua
|
||||
github.com/protolambda/ctxlock v0.1.0 h1:rCUY3+vRdcdZXqT07iXgyr744J2DU2LCBIXowYAjBCE=
|
||||
github.com/protolambda/ctxlock v0.1.0/go.mod h1:vefhX6rIZH8rsg5ZpOJfEDYQOppZi19SfPiGOFrNnwM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
@@ -1040,6 +1063,9 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 h1:RpforrEYXWkmGwJHIGnLZ3tTWStkjVVstwzNGqxX2Ds=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.3.1/go.mod h1:o8hhjkbNl2gOamKUA/eNW3xUrntHT9L4W89W1nfj43U=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
@@ -1056,6 +1082,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
@@ -1102,6 +1129,7 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
|
||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e h1:nt2877sKfojlHCTOBXbpWjBkuWKritFaGIfgQwbQUls=
|
||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e/go.mod h1:B4+Kq1u5FlULTjFSM707Q6e/cOHFv0z/6QRoxubDIQ8=
|
||||
@@ -1116,6 +1144,8 @@ github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EU
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
|
||||
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
|
||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
@@ -1177,12 +1207,12 @@ github.com/zexi/promql/v2 v2.12.1 h1:crHKpULdLLsBZ9b78Rg6qQkugzlk6BHeCj93tw/F5RU
|
||||
github.com/zexi/promql/v2 v2.12.1/go.mod h1:2UtzWZGmth95n2qdIZWpJ5yQ0cE5hEvz0dGsajI1Sqg=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v3 v3.5.0 h1:62Eh0XOro+rDwkrypAGDfgmNh5Joq+z+W9HZdlXMzek=
|
||||
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY=
|
||||
go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY=
|
||||
go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4=
|
||||
go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
@@ -1213,6 +1243,11 @@ go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
|
||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -1220,15 +1255,16 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
@@ -1261,7 +1297,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/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=
|
||||
@@ -1276,6 +1311,8 @@ golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
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=
|
||||
@@ -1310,6 +1347,7 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
@@ -1321,6 +1359,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
@@ -1384,7 +1423,6 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/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=
|
||||
@@ -1415,6 +1453,7 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/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-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1430,6 +1469,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1464,7 +1504,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
@@ -1497,7 +1536,6 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
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-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -1505,6 +1543,7 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
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-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
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=
|
||||
@@ -1526,14 +1565,18 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
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=
|
||||
@@ -1600,7 +1643,6 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240304161311-37d4d3c04a78 h1:SzXBGiWM1LNVYLCRP3e0/Gsze804l4jGoJ5lYysEO5I=
|
||||
@@ -1624,7 +1666,6 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
|
||||
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
@@ -1644,6 +1685,8 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.47.0 h1:w3mHEgOR1o52mkyCbkTM+El8DG732+Fnug4FAGhIpsk=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.47.0/go.mod h1:aHb6c4hPRANXnB64LDAKyfWotKgfRjlHv23MnahM8AI=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
|
||||
@@ -20,17 +20,16 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
"github.com/coredns/coredns/plugin"
|
||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
||||
"github.com/coredns/coredns/plugin/pkg/dnsutil"
|
||||
"github.com/coredns/coredns/plugin/pkg/fall"
|
||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
"github.com/coredns/coredns/request"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/sqlchemy"
|
||||
_ "yunion.io/x/sqlchemy/backends"
|
||||
@@ -73,7 +72,7 @@ type SRegionDNS struct {
|
||||
Fall fall.F
|
||||
Zones []string
|
||||
PrimaryZone string
|
||||
Upstream upstream.Upstream
|
||||
Upstream *upstream.Upstream
|
||||
SqlConnection string
|
||||
AuthUrl string
|
||||
AdminProject string
|
||||
@@ -135,57 +134,65 @@ func (r *SRegionDNS) initAuth() {
|
||||
}
|
||||
|
||||
func (r *SRegionDNS) ServeDNS(ctx context.Context, w dns.ResponseWriter, rmsg *dns.Msg) (int, error) {
|
||||
log.Debugf("ServeDNS: %s", jsonutils.Marshal(rmsg).String())
|
||||
|
||||
var (
|
||||
records []dns.RR
|
||||
extra []dns.RR
|
||||
err error
|
||||
)
|
||||
|
||||
state := request.Request{W: w, Req: rmsg}
|
||||
qname := state.Name()
|
||||
zone := plugin.Zones(r.Zones).Matches(qname)
|
||||
if zone == "" {
|
||||
return plugin.NextOrFailure(r.Name(), r.Next, ctx, w, rmsg)
|
||||
}
|
||||
state.Zone = zone
|
||||
|
||||
opt := plugin.Options{}
|
||||
state := request.Request{W: w, Req: rmsg, Context: ctx}
|
||||
zone := plugin.Zones(r.Zones).Matches(state.Name())
|
||||
switch state.QType() {
|
||||
case dns.TypeA:
|
||||
records, err = plugin.A(r, zone, state, nil, opt)
|
||||
records, _, err = plugin.A(ctx, r, zone, state, nil, opt)
|
||||
case dns.TypeAAAA:
|
||||
records, err = plugin.AAAA(r, zone, state, nil, opt)
|
||||
records, _, err = plugin.AAAA(ctx, r, zone, state, nil, opt)
|
||||
case dns.TypeTXT:
|
||||
records, err = plugin.TXT(r, zone, state, opt)
|
||||
records, _, err = plugin.TXT(ctx, r, zone, state, nil, opt)
|
||||
case dns.TypeCNAME:
|
||||
records, err = plugin.CNAME(r, zone, state, opt)
|
||||
records, err = plugin.CNAME(ctx, r, zone, state, opt)
|
||||
case dns.TypePTR:
|
||||
records, err = plugin.PTR(r, zone, state, opt)
|
||||
records, err = plugin.PTR(ctx, r, zone, state, opt)
|
||||
case dns.TypeMX:
|
||||
records, extra, err = plugin.MX(r, zone, state, opt)
|
||||
records, extra, err = plugin.MX(ctx, r, zone, state, opt)
|
||||
case dns.TypeSRV:
|
||||
records, extra, err = plugin.SRV(r, zone, state, opt)
|
||||
records, extra, err = plugin.SRV(ctx, r, zone, state, opt)
|
||||
case dns.TypeSOA:
|
||||
records, err = plugin.SOA(r, zone, state, opt)
|
||||
records, err = plugin.SOA(ctx, r, zone, state, opt)
|
||||
case dns.TypeNS:
|
||||
if state.Name() == zone {
|
||||
records, extra, err = plugin.NS(r, zone, state, opt)
|
||||
records, extra, err = plugin.NS(ctx, r, zone, state, opt)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
log.Warningf("Not processed state: %#v", state)
|
||||
// Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
|
||||
_, err = plugin.A(r, zone, state, nil, opt)
|
||||
_, _, err = plugin.A(ctx, r, zone, state, nil, opt)
|
||||
}
|
||||
|
||||
if err == errCallNext {
|
||||
if r.Fall.Through(state.Name()) {
|
||||
return plugin.NextOrFailure(r.Name(), r.Next, ctx, w, rmsg)
|
||||
}
|
||||
return plugin.BackendError(r, zone, dns.RcodeNameError, state, nil /* err */, opt)
|
||||
return plugin.BackendError(ctx, r, zone, dns.RcodeNameError, state, nil /* err */, opt)
|
||||
} else if err == errRefused {
|
||||
return plugin.BackendError(r, zone, dns.RcodeRefused, state, err, opt)
|
||||
return plugin.BackendError(ctx, r, zone, dns.RcodeRefused, state, err, opt)
|
||||
} else if err == errNotFound {
|
||||
return plugin.BackendError(r, zone, dns.RcodeNameError, state, err, opt)
|
||||
return plugin.BackendError(ctx, r, zone, dns.RcodeNameError, state, err, opt)
|
||||
}
|
||||
|
||||
if len(records) == 0 {
|
||||
return plugin.BackendError(r, zone, dns.RcodeNameError, state, err, opt)
|
||||
return plugin.BackendError(ctx, r, zone, dns.RcodeNameError, state, err, opt)
|
||||
}
|
||||
|
||||
m := new(dns.Msg)
|
||||
@@ -207,7 +214,7 @@ var (
|
||||
)
|
||||
|
||||
// Services implements the ServiceBackend interface
|
||||
func (r *SRegionDNS) Services(state request.Request, exact bool, opt plugin.Options) ([]msg.Service, error) {
|
||||
func (r *SRegionDNS) Services(ctx context.Context, state request.Request, exact bool, opt plugin.Options) ([]msg.Service, error) {
|
||||
var services []msg.Service
|
||||
var err error
|
||||
|
||||
@@ -255,7 +262,7 @@ func (r *SRegionDNS) Services(state request.Request, exact bool, opt plugin.Opti
|
||||
return nil, errRefused
|
||||
}
|
||||
|
||||
services, err = r.Records(state, false)
|
||||
services, err = r.Records(ctx, state, false)
|
||||
if err != nil {
|
||||
// log.Errorf("Records %s fail: %s", state.Name(), err)
|
||||
return nil, err
|
||||
@@ -264,8 +271,8 @@ func (r *SRegionDNS) Services(state request.Request, exact bool, opt plugin.Opti
|
||||
}
|
||||
|
||||
// Lookup implements the ServiceBackend interface
|
||||
func (r *SRegionDNS) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) {
|
||||
return r.Upstream.Lookup(state, name, typ)
|
||||
func (r *SRegionDNS) Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error) {
|
||||
return r.Upstream.Lookup(ctx, state, name, typ)
|
||||
}
|
||||
|
||||
// IsNameError implements the ServiceBackend interface
|
||||
@@ -274,7 +281,7 @@ func (r *SRegionDNS) IsNameError(err error) bool {
|
||||
}
|
||||
|
||||
// Records looks up records in region mysql
|
||||
func (r *SRegionDNS) Records(state request.Request, exact bool) ([]msg.Service, error) {
|
||||
func (r *SRegionDNS) Records(ctx context.Context, state request.Request, exact bool) ([]msg.Service, error) {
|
||||
req := parseRequest(state)
|
||||
|
||||
if r.InCloudOnly && !req.srcInCloud {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/coredns/coredns/plugin"
|
||||
@@ -26,10 +27,10 @@ import (
|
||||
)
|
||||
|
||||
// Reverse implements the ServiceBackend interface
|
||||
func (r *SRegionDNS) Reverse(state request.Request, exact bool, opt plugin.Options) (services []msg.Service, err error) {
|
||||
func (r *SRegionDNS) Reverse(ctx context.Context, state request.Request, exact bool, opt plugin.Options) (services []msg.Service, err error) {
|
||||
ip := dnsutil.ExtractAddressFromReverse(state.Name())
|
||||
if ip == "" {
|
||||
_, e := r.Records(state, exact)
|
||||
_, e := r.Records(ctx, state, exact)
|
||||
return nil, e
|
||||
}
|
||||
records, err := r.getNameForIp(ip, state)
|
||||
|
||||
237
pkg/dns/setup.go
237
pkg/dns/setup.go
@@ -17,13 +17,14 @@ package dns
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
"github.com/coredns/coredns/core/dnsserver"
|
||||
"github.com/coredns/coredns/plugin"
|
||||
"github.com/coredns/coredns/plugin/pkg/fall"
|
||||
clog "github.com/coredns/coredns/plugin/pkg/log"
|
||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/regutils"
|
||||
|
||||
@@ -31,67 +32,20 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/identity"
|
||||
)
|
||||
|
||||
var log = clog.NewWithPlugin(PluginName)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin(PluginName, caddy.Plugin{
|
||||
ServerType: "dns",
|
||||
Action: setup,
|
||||
})
|
||||
plugin.Register(PluginName, setup)
|
||||
}
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
rDNS, err := regionDNSParse(c)
|
||||
if err != nil {
|
||||
return plugin.Error(PluginName, err)
|
||||
}
|
||||
|
||||
rDNS.initAuth()
|
||||
|
||||
conf, err := identity.ServicesV3.GetConfig(rDNS.getAdminSession(context.Background()), apis.SERVICE_TYPE_REGION)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetConfig")
|
||||
}
|
||||
|
||||
if conf.Contains("dns_domain") {
|
||||
rDNS.PrimaryZone, _ = conf.GetString("dns_domain")
|
||||
}
|
||||
log.Infof("use dns_domain %q", rDNS.PrimaryZone)
|
||||
|
||||
if len(rDNS.PrimaryZone) > 0 && !regutils.MatchDomainName(rDNS.PrimaryZone) {
|
||||
return errors.Wrapf(errors.ErrInvalidFormat, "dns_domain %q invalid", rDNS.PrimaryZone)
|
||||
}
|
||||
if len(rDNS.PrimaryZone) > 0 {
|
||||
if r := rDNS.PrimaryZone[len(rDNS.PrimaryZone)-1]; r != '.' {
|
||||
rDNS.PrimaryZone += "."
|
||||
}
|
||||
} else {
|
||||
rDNS.PrimaryZone = ""
|
||||
}
|
||||
rDNS.primaryZoneLabelCount = dns.CountLabel(rDNS.PrimaryZone)
|
||||
|
||||
err = rDNS.initDB(c)
|
||||
if err != nil {
|
||||
return plugin.Error(PluginName, err)
|
||||
}
|
||||
|
||||
/*if !rDNS.K8sSkip {
|
||||
go rDNS.initK8s()
|
||||
}*/
|
||||
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
rDNS.Next = next
|
||||
return rDNS
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func regionDNSParse(c *caddy.Controller) (*SRegionDNS, error) {
|
||||
return parseConfig(c)
|
||||
}
|
||||
|
||||
func parseConfig(c *caddy.Controller) (*SRegionDNS, error) {
|
||||
rDNS := New()
|
||||
for c.Next() {
|
||||
log.Infof("setup: %s", PluginName)
|
||||
|
||||
var fallF fall.F
|
||||
upStream := upstream.New()
|
||||
|
||||
rDNS := New()
|
||||
rDNS.Zones = c.RemainingArgs()
|
||||
if len(rDNS.Zones) == 0 {
|
||||
rDNS.Zones = make([]string, len(c.ServerBlockKeys))
|
||||
@@ -101,78 +55,107 @@ func parseConfig(c *caddy.Controller) (*SRegionDNS, error) {
|
||||
rDNS.Zones[i] = plugin.Host(str).Normalize()
|
||||
}
|
||||
|
||||
if c.NextBlock() {
|
||||
for {
|
||||
switch c.Val() {
|
||||
case "dns_domain":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
rDNS.PrimaryZone = c.Val()
|
||||
case "fallthrough":
|
||||
rDNS.Fall.SetZonesFromArgs(c.RemainingArgs())
|
||||
case "sql_connection":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
rDNS.SqlConnection = c.Val()
|
||||
case "auth_url":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
rDNS.AuthUrl = c.Val()
|
||||
case "admin_project":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
rDNS.AdminProject = c.Val()
|
||||
case "admin_project_domain":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
rDNS.AdminProjectDomain = c.Val()
|
||||
case "admin_user":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
rDNS.AdminUser = c.Val()
|
||||
case "admin_domain":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
rDNS.AdminDomain = c.Val()
|
||||
case "admin_password":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
rDNS.AdminPassword = c.Val()
|
||||
case "region":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
rDNS.Region = c.Val()
|
||||
case "upstream":
|
||||
args := c.RemainingArgs()
|
||||
u, err := upstream.New(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rDNS.Upstream = u
|
||||
case "in_cloud_only":
|
||||
rDNS.InCloudOnly = true
|
||||
// case "k8s_skip":
|
||||
// rDNS.K8sSkip = true
|
||||
default:
|
||||
if c.Val() != "}" {
|
||||
return nil, c.Errf("unknown property %q", c.Val())
|
||||
}
|
||||
for c.NextBlock() {
|
||||
switch c.Val() {
|
||||
case "dns_domain":
|
||||
if !c.NextArg() {
|
||||
return plugin.Error(PluginName+".dns_domain", c.ArgErr())
|
||||
}
|
||||
|
||||
if !c.Next() {
|
||||
break
|
||||
rDNS.PrimaryZone = c.Val()
|
||||
case "sql_connection":
|
||||
if !c.NextArg() {
|
||||
return plugin.Error(PluginName+".sql_connection", c.ArgErr())
|
||||
}
|
||||
rDNS.SqlConnection = c.Val()
|
||||
case "auth_url":
|
||||
if !c.NextArg() {
|
||||
return plugin.Error(PluginName+".auth_url", c.ArgErr())
|
||||
}
|
||||
rDNS.AuthUrl = c.Val()
|
||||
case "admin_project":
|
||||
if !c.NextArg() {
|
||||
return plugin.Error(PluginName+".admin_project", c.ArgErr())
|
||||
}
|
||||
rDNS.AdminProject = c.Val()
|
||||
case "admin_project_domain":
|
||||
if !c.NextArg() {
|
||||
return plugin.Error(PluginName+".admin_project_domain", c.ArgErr())
|
||||
}
|
||||
rDNS.AdminProjectDomain = c.Val()
|
||||
case "admin_user":
|
||||
if !c.NextArg() {
|
||||
return plugin.Error(PluginName+".admin_user", c.ArgErr())
|
||||
}
|
||||
rDNS.AdminUser = c.Val()
|
||||
case "admin_domain":
|
||||
if !c.NextArg() {
|
||||
return plugin.Error(PluginName+".admin_domain", c.ArgErr())
|
||||
}
|
||||
rDNS.AdminDomain = c.Val()
|
||||
case "admin_password":
|
||||
if !c.NextArg() {
|
||||
return plugin.Error(PluginName+".admin_password", c.ArgErr())
|
||||
}
|
||||
rDNS.AdminPassword = c.Val()
|
||||
case "region":
|
||||
if !c.NextArg() {
|
||||
return plugin.Error(PluginName+".region", c.ArgErr())
|
||||
}
|
||||
rDNS.Region = c.Val()
|
||||
case "in_cloud_only":
|
||||
rDNS.InCloudOnly = true
|
||||
case "upstream":
|
||||
c.RemainingArgs()
|
||||
case "fallthrough":
|
||||
fallF.SetZonesFromArgs(c.RemainingArgs())
|
||||
default:
|
||||
return plugin.Error(PluginName, c.Errf("unknown property %q", c.Val()))
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
rDNS.initAuth()
|
||||
|
||||
conf, err := identity.ServicesV3.GetConfig(rDNS.getAdminSession(ctx), apis.SERVICE_TYPE_REGION)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return errors.Wrap(err, "GetConfig")
|
||||
}
|
||||
|
||||
if conf.Contains("dns_domain") {
|
||||
rDNS.PrimaryZone, _ = conf.GetString("dns_domain")
|
||||
}
|
||||
log.Infof("use dns_domain %q", rDNS.PrimaryZone)
|
||||
|
||||
if len(rDNS.PrimaryZone) > 0 && !regutils.MatchDomainName(rDNS.PrimaryZone) {
|
||||
cancel()
|
||||
return errors.Wrapf(errors.ErrInvalidFormat, "dns_domain %q invalid", rDNS.PrimaryZone)
|
||||
}
|
||||
if len(rDNS.PrimaryZone) > 0 {
|
||||
if r := rDNS.PrimaryZone[len(rDNS.PrimaryZone)-1]; r != '.' {
|
||||
rDNS.PrimaryZone += "."
|
||||
}
|
||||
} else {
|
||||
rDNS.PrimaryZone = ""
|
||||
}
|
||||
rDNS.primaryZoneLabelCount = dns.CountLabel(rDNS.PrimaryZone)
|
||||
|
||||
err = rDNS.initDB(c)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return plugin.Error(PluginName, err)
|
||||
}
|
||||
|
||||
rDNS.Fall = fallF
|
||||
rDNS.Upstream = upStream
|
||||
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
rDNS.Next = next
|
||||
return rDNS
|
||||
})
|
||||
c.OnShutdown(func() error { cancel(); return nil })
|
||||
}
|
||||
return rDNS, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# The standard Go .gitignore file follows. (Sourced from: github.com/github/gitignore/master/Go.gitignore)
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.test
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
.vagrant
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
@@ -22,6 +24,9 @@ _cgo_export.*
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
coverage.txt
|
||||
profile.out
|
||||
# go-autorest specific
|
||||
vendor/
|
||||
autorest/azure/example/example
|
||||
1004
vendor/github.com/Azure/go-autorest/CHANGELOG.md
generated
vendored
Normal file
1004
vendor/github.com/Azure/go-autorest/CHANGELOG.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
23
vendor/github.com/Azure/go-autorest/GNUmakefile
generated
vendored
Normal file
23
vendor/github.com/Azure/go-autorest/GNUmakefile
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
DIR?=./autorest/
|
||||
|
||||
default: build
|
||||
|
||||
build: fmt
|
||||
go install $(DIR)
|
||||
|
||||
test:
|
||||
go test $(DIR) || exit 1
|
||||
|
||||
vet:
|
||||
@echo "go vet ."
|
||||
@go vet $(DIR)... ; if [ $$? -eq 1 ]; then \
|
||||
echo ""; \
|
||||
echo "Vet found suspicious constructs. Please check the reported constructs"; \
|
||||
echo "and fix them if necessary before submitting the code for review."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
fmt:
|
||||
gofmt -w $(DIR)
|
||||
|
||||
.PHONY: build test vet fmt
|
||||
324
vendor/github.com/Azure/go-autorest/Gopkg.lock
generated
vendored
Normal file
324
vendor/github.com/Azure/go-autorest/Gopkg.lock
generated
vendored
Normal file
@@ -0,0 +1,324 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:892e39e5c083d0943f1e80ab8351690f183c6a5ab24e1d280adcad424c26255e"
|
||||
name = "contrib.go.opencensus.io/exporter/ocagent"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "a8a6f458bbc1d5042322ad1f9b65eeb0b69be9ea"
|
||||
version = "v0.6.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8f5acd4d4462b5136af644d25101f0968a7a94ee90fcb2059cec5b7cc42e0b20"
|
||||
name = "github.com/census-instrumentation/opencensus-proto"
|
||||
packages = [
|
||||
"gen-go/agent/common/v1",
|
||||
"gen-go/agent/metrics/v1",
|
||||
"gen-go/agent/trace/v1",
|
||||
"gen-go/metrics/v1",
|
||||
"gen-go/resource/v1",
|
||||
"gen-go/trace/v1",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "d89fa54de508111353cb0b06403c00569be780d8"
|
||||
version = "v0.2.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
pruneopts = "UT"
|
||||
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:76dc72490af7174349349838f2fe118996381b31ea83243812a97e5a0fd5ed55"
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "06ea1031745cb8b3dab3f6a236daf2b0aa468b7e"
|
||||
version = "v3.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cf0d2e435fd4ce45b789e93ef24b5f08e86be0e9807a16beb3694e2d8c9af965"
|
||||
name = "github.com/dimchansky/utfbom"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "d2133a1ce379ef6fa992b0514a77146c60db9d1c"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:b7cb6054d3dff43b38ad2e92492f220f57ae6087ee797dca298139776749ace8"
|
||||
name = "github.com/golang/groupcache"
|
||||
packages = ["lru"]
|
||||
pruneopts = "UT"
|
||||
revision = "611e8accdfc92c4187d399e95ce826046d4c8d73"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e3839df32927e8d3403cd5aa7253d966e8ff80fc8f10e2e35d146461cd83fcfa"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"descriptor",
|
||||
"jsonpb",
|
||||
"proto",
|
||||
"protoc-gen-go/descriptor",
|
||||
"ptypes",
|
||||
"ptypes/any",
|
||||
"ptypes/duration",
|
||||
"ptypes/struct",
|
||||
"ptypes/timestamp",
|
||||
"ptypes/wrappers",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "6c65a5562fc06764971b7c5d05c76c75e84bdbf7"
|
||||
version = "v1.3.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c560cd79300fac84f124b96225181a637a70b60155919a3c36db50b7cca6b806"
|
||||
name = "github.com/grpc-ecosystem/grpc-gateway"
|
||||
packages = [
|
||||
"internal",
|
||||
"runtime",
|
||||
"utilities",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f7120437bb4f6c71f7f5076ad65a45310de2c009"
|
||||
version = "v1.12.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5d231480e1c64a726869bc4142d270184c419749d34f167646baa21008eb0a79"
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "af06845cf3004701891bf4fdb884bfe4920b3727"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
pruneopts = "UT"
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:99d32780e5238c2621fff621123997c3e3cca96db8be13179013aea77dfab551"
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"require",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "221dbe5ed46703ee255b1da0dec05086f5035f62"
|
||||
version = "v1.4.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7c5e00383399fe13de0b4b65c9fdde16275407ce8ac02d867eafeaa916edcc71"
|
||||
name = "go.opencensus.io"
|
||||
packages = [
|
||||
".",
|
||||
"internal",
|
||||
"internal/tagencoding",
|
||||
"metric/metricdata",
|
||||
"metric/metricproducer",
|
||||
"plugin/ocgrpc",
|
||||
"plugin/ochttp",
|
||||
"plugin/ochttp/propagation/b3",
|
||||
"plugin/ochttp/propagation/tracecontext",
|
||||
"resource",
|
||||
"stats",
|
||||
"stats/internal",
|
||||
"stats/view",
|
||||
"tag",
|
||||
"trace",
|
||||
"trace/internal",
|
||||
"trace/propagation",
|
||||
"trace/tracestate",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "aad2c527c5defcf89b5afab7f37274304195a6b2"
|
||||
version = "v0.22.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:f604f5e2ee721b6757d962dfe7bab4f28aae50c456e39cfb2f3819762a44a6ae"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"pkcs12",
|
||||
"pkcs12/internal/rc2",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "e9b2fee46413994441b28dfca259d911d963dfed"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:334b27eac455cb6567ea28cd424230b07b1a64334a2f861a8075ac26ce10af43"
|
||||
name = "golang.org/x/lint"
|
||||
packages = [
|
||||
".",
|
||||
"golint",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "fdd1cda4f05fd1fd86124f0ef9ce31a0b72c8448"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:257a75d024975428ab9192bfc334c3490882f8cb21322ea5784ca8eca000a910"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"http/httpguts",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
"internal/timeseries",
|
||||
"trace",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "1ddd1de85cb0337b623b740a609d35817d516a8d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:382bb5a7fb4034db3b6a2d19e5a4a6bcf52f4750530603c01ca18a172fa3089b"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["semaphore"]
|
||||
pruneopts = "UT"
|
||||
revision = "cd5d95a43a6e21273425c7ae415d3df9ea832eeb"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:4da420ceda5f68e8d748aa2169d0ed44ffadb1bbd6537cf778a49563104189b8"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
pruneopts = "UT"
|
||||
revision = "ce4227a45e2eb77e5c847278dcc6a626742e2945"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8d8faad6b12a3a4c819a3f9618cb6ee1fa1cfc33253abeeea8b55336721e3405"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
"collate/build",
|
||||
"internal/colltab",
|
||||
"internal/gen",
|
||||
"internal/language",
|
||||
"internal/language/compact",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"language",
|
||||
"secure/bidirule",
|
||||
"transform",
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475"
|
||||
version = "v0.3.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:4eb5ea8395fb60212dd58b92c9db80bab59d5e99c7435f9a6a0a528c373b60e7"
|
||||
name = "golang.org/x/tools"
|
||||
packages = [
|
||||
"go/ast/astutil",
|
||||
"go/gcexportdata",
|
||||
"go/internal/gcimporter",
|
||||
"go/types/typeutil",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "259af5ff87bdcd4abf2ecda8edc3f13f04f26a42"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:964bb30febc27fabfbec4759fa530c6ec35e77a7c85fed90b9317ea39a054877"
|
||||
name = "google.golang.org/api"
|
||||
packages = ["support/bundler"]
|
||||
pruneopts = "UT"
|
||||
revision = "8a410c21381766a810817fd6200fce8838ecb277"
|
||||
version = "v0.14.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a8d5c2c6e746b3485e36908ab2a9e3d77b86b81f8156d88403c7d2b462431dfd"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = [
|
||||
"googleapis/api/httpbody",
|
||||
"googleapis/rpc/status",
|
||||
"protobuf/field_mask",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "51378566eb590fa106d1025ea12835a4416dda84"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b59ce3ddb11daeeccccc9cb3183b58ebf8e9a779f1c853308cd91612e817a301"
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [
|
||||
".",
|
||||
"backoff",
|
||||
"balancer",
|
||||
"balancer/base",
|
||||
"balancer/roundrobin",
|
||||
"binarylog/grpc_binarylog_v1",
|
||||
"codes",
|
||||
"connectivity",
|
||||
"credentials",
|
||||
"credentials/internal",
|
||||
"encoding",
|
||||
"encoding/proto",
|
||||
"grpclog",
|
||||
"internal",
|
||||
"internal/backoff",
|
||||
"internal/balancerload",
|
||||
"internal/binarylog",
|
||||
"internal/buffer",
|
||||
"internal/channelz",
|
||||
"internal/envconfig",
|
||||
"internal/grpcrand",
|
||||
"internal/grpcsync",
|
||||
"internal/resolver/dns",
|
||||
"internal/resolver/passthrough",
|
||||
"internal/syscall",
|
||||
"internal/transport",
|
||||
"keepalive",
|
||||
"metadata",
|
||||
"naming",
|
||||
"peer",
|
||||
"resolver",
|
||||
"serviceconfig",
|
||||
"stats",
|
||||
"status",
|
||||
"tap",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "1a3960e4bd028ac0cec0a2afd27d7d8e67c11514"
|
||||
version = "v1.25.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b75b3deb2bce8bc079e16bb2aecfe01eb80098f5650f9e93e5643ca8b7b73737"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "1f64d6156d11335c3f22d9330b0ad14fc1e789ce"
|
||||
version = "v2.2.7"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"contrib.go.opencensus.io/exporter/ocagent",
|
||||
"github.com/dgrijalva/jwt-go",
|
||||
"github.com/dimchansky/utfbom",
|
||||
"github.com/mitchellh/go-homedir",
|
||||
"github.com/stretchr/testify/require",
|
||||
"go.opencensus.io/plugin/ochttp",
|
||||
"go.opencensus.io/plugin/ochttp/propagation/tracecontext",
|
||||
"go.opencensus.io/stats/view",
|
||||
"go.opencensus.io/trace",
|
||||
"golang.org/x/crypto/pkcs12",
|
||||
"golang.org/x/lint/golint",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
59
vendor/github.com/Azure/go-autorest/Gopkg.toml
generated
vendored
Normal file
59
vendor/github.com/Azure/go-autorest/Gopkg.toml
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
required = ["golang.org/x/lint/golint"]
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "contrib.go.opencensus.io/exporter/ocagent"
|
||||
version = "0.6.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
version = "3.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/dimchansky/utfbom"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "go.opencensus.io"
|
||||
version = "0.22.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
191
vendor/github.com/Azure/go-autorest/LICENSE
generated
vendored
Normal file
191
vendor/github.com/Azure/go-autorest/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2015 Microsoft Corporation
|
||||
|
||||
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.
|
||||
165
vendor/github.com/Azure/go-autorest/README.md
generated
vendored
Normal file
165
vendor/github.com/Azure/go-autorest/README.md
generated
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
# go-autorest
|
||||
|
||||
[](https://godoc.org/github.com/Azure/go-autorest/autorest)
|
||||
[](https://dev.azure.com/azure-sdk/public/_build/latest?definitionId=625&branchName=master)
|
||||
[](https://goreportcard.com/report/Azure/go-autorest)
|
||||
|
||||
Package go-autorest provides an HTTP request client for use with [Autorest](https://github.com/Azure/autorest.go)-generated API client packages.
|
||||
|
||||
An authentication client tested with Azure Active Directory (AAD) is also
|
||||
provided in this repo in the package
|
||||
`github.com/Azure/go-autorest/autorest/adal`. Despite its name, this package
|
||||
is maintained only as part of the Azure Go SDK and is not related to other
|
||||
"ADAL" libraries in [github.com/AzureAD](https://github.com/AzureAD).
|
||||
|
||||
## Overview
|
||||
|
||||
Package go-autorest implements an HTTP request pipeline suitable for use across
|
||||
multiple goroutines and provides the shared routines used by packages generated
|
||||
by [Autorest](https://github.com/Azure/autorest.go).
|
||||
|
||||
The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending,
|
||||
and Responding. A typical pattern is:
|
||||
|
||||
```go
|
||||
req, err := Prepare(&http.Request{},
|
||||
token.WithAuthorization())
|
||||
|
||||
resp, err := Send(req,
|
||||
WithLogging(logger),
|
||||
DoErrorIfStatusCode(http.StatusInternalServerError),
|
||||
DoCloseIfError(),
|
||||
DoRetryForAttempts(5, time.Second))
|
||||
|
||||
err = Respond(resp,
|
||||
ByDiscardingBody(),
|
||||
ByClosing())
|
||||
```
|
||||
|
||||
Each phase relies on decorators to modify and / or manage processing. Decorators may first modify
|
||||
and then pass the data along, pass the data first and then modify the result, or wrap themselves
|
||||
around passing the data (such as a logger might do). Decorators run in the order provided. For
|
||||
example, the following:
|
||||
|
||||
```go
|
||||
req, err := Prepare(&http.Request{},
|
||||
WithBaseURL("https://microsoft.com/"),
|
||||
WithPath("a"),
|
||||
WithPath("b"),
|
||||
WithPath("c"))
|
||||
```
|
||||
|
||||
will set the URL to:
|
||||
|
||||
```
|
||||
https://microsoft.com/a/b/c
|
||||
```
|
||||
|
||||
Preparers and Responders may be shared and re-used (assuming the underlying decorators support
|
||||
sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders
|
||||
shared among multiple go-routines, and a single Sender shared among multiple sending go-routines,
|
||||
all bound together by means of input / output channels.
|
||||
|
||||
Decorators hold their passed state within a closure (such as the path components in the example
|
||||
above). Be careful to share Preparers and Responders only in a context where such held state
|
||||
applies. For example, it may not make sense to share a Preparer that applies a query string from a
|
||||
fixed set of values. Similarly, sharing a Responder that reads the response body into a passed
|
||||
struct (e.g., `ByUnmarshallingJson`) is likely incorrect.
|
||||
|
||||
Errors raised by autorest objects and methods will conform to the `autorest.Error` interface.
|
||||
|
||||
See the included examples for more detail. For details on the suggested use of this package by
|
||||
generated clients, see the Client described below.
|
||||
|
||||
## Helpers
|
||||
|
||||
### Handling Swagger Dates
|
||||
|
||||
The Swagger specification (https://swagger.io) that drives AutoRest
|
||||
(https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The
|
||||
github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure correct
|
||||
parsing and formatting.
|
||||
|
||||
### Handling Empty Values
|
||||
|
||||
In JSON, missing values have different semantics than empty values. This is especially true for
|
||||
services using the HTTP PATCH verb. The JSON submitted with a PATCH request generally contains
|
||||
only those values to modify. Missing values are to be left unchanged. Developers, then, require a
|
||||
means to both specify an empty value and to leave the value out of the submitted JSON.
|
||||
|
||||
The Go JSON package (`encoding/json`) supports the `omitempty` tag. When specified, it omits
|
||||
empty values from the rendered JSON. Since Go defines default values for all base types (such as ""
|
||||
for string and 0 for int) and provides no means to mark a value as actually empty, the JSON package
|
||||
treats default values as meaning empty, omitting them from the rendered JSON. This means that, using
|
||||
the Go base types encoded through the default JSON package, it is not possible to create JSON to
|
||||
clear a value at the server.
|
||||
|
||||
The workaround within the Go community is to use pointers to base types in lieu of base types within
|
||||
structures that map to JSON. For example, instead of a value of type `string`, the workaround uses
|
||||
`*string`. While this enables distinguishing empty values from those to be unchanged, creating
|
||||
pointers to a base type (notably constant, in-line values) requires additional variables. This, for
|
||||
example,
|
||||
|
||||
```go
|
||||
s := struct {
|
||||
S *string
|
||||
}{ S: &"foo" }
|
||||
```
|
||||
fails, while, this
|
||||
|
||||
```go
|
||||
v := "foo"
|
||||
s := struct {
|
||||
S *string
|
||||
}{ S: &v }
|
||||
```
|
||||
succeeds.
|
||||
|
||||
To ease using pointers, the subpackage `to` contains helpers that convert to and from pointers for
|
||||
Go base types which have Swagger analogs. It also provides a helper that converts between
|
||||
`map[string]string` and `map[string]*string`, enabling the JSON to specify that the value
|
||||
associated with a key should be cleared. With the helpers, the previous example becomes
|
||||
|
||||
```go
|
||||
s := struct {
|
||||
S *string
|
||||
}{ S: to.StringPtr("foo") }
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/Azure/go-autorest/autorest
|
||||
go get github.com/Azure/go-autorest/autorest/azure
|
||||
go get github.com/Azure/go-autorest/autorest/date
|
||||
go get github.com/Azure/go-autorest/autorest/to
|
||||
```
|
||||
|
||||
### Using with Go Modules
|
||||
In [v12.0.1](https://github.com/Azure/go-autorest/pull/386), this repository introduced the following modules.
|
||||
|
||||
- autorest/adal
|
||||
- autorest/azure/auth
|
||||
- autorest/azure/cli
|
||||
- autorest/date
|
||||
- autorest/mocks
|
||||
- autorest/to
|
||||
- autorest/validation
|
||||
- autorest
|
||||
- logger
|
||||
- tracing
|
||||
|
||||
Tagging cumulative SDK releases as a whole (e.g. `v12.3.0`) is still enabled to support consumers of this repo that have not yet migrated to modules.
|
||||
|
||||
## License
|
||||
|
||||
See LICENSE file.
|
||||
|
||||
-----
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of
|
||||
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
|
||||
see the [Code of Conduct
|
||||
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
|
||||
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
|
||||
questions or comments.
|
||||
4
vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go
generated
vendored
4
vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go
generated
vendored
@@ -222,6 +222,10 @@ func CheckForUserCompletionWithContext(ctx context.Context, sender Sender, code
|
||||
case "code_expired":
|
||||
return nil, ErrDeviceCodeExpired
|
||||
default:
|
||||
// return a more meaningful error message if available
|
||||
if token.ErrorDescription != nil {
|
||||
return nil, fmt.Errorf("%s %s: %s", logPrefix, *token.Error, *token.ErrorDescription)
|
||||
}
|
||||
return nil, ErrDeviceGeneric
|
||||
}
|
||||
}
|
||||
|
||||
5
vendor/github.com/Azure/go-autorest/autorest/adal/go_mod_tidy_hack.go
generated
vendored
5
vendor/github.com/Azure/go-autorest/autorest/adal/go_mod_tidy_hack.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build modhack
|
||||
// +build modhack
|
||||
|
||||
package adal
|
||||
@@ -16,9 +17,9 @@ package adal
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This file, and the github.com/Azure/go-autorest/autorest import, won't actually become part of
|
||||
// This file, and the github.com/Azure/go-autorest import, won't actually become part of
|
||||
// the resultant binary.
|
||||
|
||||
// Necessary for safely adding multi-module repo.
|
||||
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
|
||||
import _ "github.com/Azure/go-autorest/autorest"
|
||||
import _ "github.com/Azure/go-autorest"
|
||||
|
||||
62
vendor/github.com/Azure/go-autorest/autorest/adal/persist.go
generated
vendored
62
vendor/github.com/Azure/go-autorest/autorest/adal/persist.go
generated
vendored
@@ -15,11 +15,24 @@ package adal
|
||||
// limitations under the License.
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrMissingCertificate is returned when no local certificate is found in the provided PFX data.
|
||||
ErrMissingCertificate = errors.New("adal: certificate missing")
|
||||
|
||||
// ErrMissingPrivateKey is returned when no private key is found in the provided PFX data.
|
||||
ErrMissingPrivateKey = errors.New("adal: private key missing")
|
||||
)
|
||||
|
||||
// LoadToken restores a Token object from a file located at 'path'.
|
||||
@@ -71,3 +84,52 @@ func SaveToken(path string, mode os.FileMode, token Token) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodePfxCertificateData extracts the x509 certificate and RSA private key from the provided PFX data.
|
||||
// The PFX data must contain a private key along with a certificate whose public key matches that of the
|
||||
// private key or an error is returned.
|
||||
// If the private key is not password protected pass the empty string for password.
|
||||
func DecodePfxCertificateData(pfxData []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||
blocks, err := pkcs12.ToPEM(pfxData, password)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// first extract the private key
|
||||
var priv *rsa.PrivateKey
|
||||
for _, block := range blocks {
|
||||
if block.Type == "PRIVATE KEY" {
|
||||
priv, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if priv == nil {
|
||||
return nil, nil, ErrMissingPrivateKey
|
||||
}
|
||||
// now find the certificate with the matching public key of our private key
|
||||
var cert *x509.Certificate
|
||||
for _, block := range blocks {
|
||||
if block.Type == "CERTIFICATE" {
|
||||
pcert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
certKey, ok := pcert.PublicKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
// keep looking
|
||||
continue
|
||||
}
|
||||
if priv.E == certKey.E && priv.N.Cmp(certKey.N) == 0 {
|
||||
// found a match
|
||||
cert = pcert
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if cert == nil {
|
||||
return nil, nil, ErrMissingCertificate
|
||||
}
|
||||
return cert, priv, nil
|
||||
}
|
||||
|
||||
22
vendor/github.com/Azure/go-autorest/autorest/adal/sender.go
generated
vendored
22
vendor/github.com/Azure/go-autorest/autorest/adal/sender.go
generated
vendored
@@ -16,9 +16,11 @@ package adal
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/tracing"
|
||||
)
|
||||
@@ -28,6 +30,7 @@ const (
|
||||
mimeTypeFormPost = "application/x-www-form-urlencoded"
|
||||
)
|
||||
|
||||
// DO NOT ACCESS THIS DIRECTLY. go through sender()
|
||||
var defaultSender Sender
|
||||
var defaultSenderInit = &sync.Once{}
|
||||
|
||||
@@ -71,15 +74,18 @@ func sender() Sender {
|
||||
// note that we can't init defaultSender in init() since it will
|
||||
// execute before calling code has had a chance to enable tracing
|
||||
defaultSenderInit.Do(func() {
|
||||
// Use behaviour compatible with DefaultTransport, but require TLS minimum version.
|
||||
defaultTransport := http.DefaultTransport.(*http.Transport)
|
||||
// copied from http.DefaultTransport with a TLS minimum version.
|
||||
transport := &http.Transport{
|
||||
Proxy: defaultTransport.Proxy,
|
||||
DialContext: defaultTransport.DialContext,
|
||||
MaxIdleConns: defaultTransport.MaxIdleConns,
|
||||
IdleConnTimeout: defaultTransport.IdleConnTimeout,
|
||||
TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout,
|
||||
ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
},
|
||||
|
||||
443
vendor/github.com/Azure/go-autorest/autorest/adal/token.go
generated
vendored
443
vendor/github.com/Azure/go-autorest/autorest/adal/token.go
generated
vendored
@@ -30,12 +30,14 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/date"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/Azure/go-autorest/logger"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -62,14 +64,29 @@ const (
|
||||
// msiEndpoint is the well known endpoint for getting MSI authentications tokens
|
||||
msiEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token"
|
||||
|
||||
// the API version to use for the MSI endpoint
|
||||
msiAPIVersion = "2018-02-01"
|
||||
|
||||
// the default number of attempts to refresh an MSI authentication token
|
||||
defaultMaxMSIRefreshAttempts = 5
|
||||
|
||||
// asMSIEndpointEnv is the environment variable used to store the endpoint on App Service and Functions
|
||||
asMSIEndpointEnv = "MSI_ENDPOINT"
|
||||
msiEndpointEnv = "MSI_ENDPOINT"
|
||||
|
||||
// asMSISecretEnv is the environment variable used to store the request secret on App Service and Functions
|
||||
asMSISecretEnv = "MSI_SECRET"
|
||||
msiSecretEnv = "MSI_SECRET"
|
||||
|
||||
// the API version to use for the legacy App Service MSI endpoint
|
||||
appServiceAPIVersion2017 = "2017-09-01"
|
||||
|
||||
// secret header used when authenticating against app service MSI endpoint
|
||||
secretHeader = "Secret"
|
||||
|
||||
// the format for expires_on in UTC with AM/PM
|
||||
expiresOnDateFormatPM = "1/2/2006 15:04:05 PM +00:00"
|
||||
|
||||
// the format for expires_on in UTC without AM/PM
|
||||
expiresOnDateFormat = "1/2/2006 15:04:05 +00:00"
|
||||
)
|
||||
|
||||
// OAuthTokenProvider is an interface which should be implemented by an access token retriever
|
||||
@@ -276,6 +293,8 @@ func (secret ServicePrincipalCertificateSecret) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// ServicePrincipalMSISecret implements ServicePrincipalSecret for machines running the MSI Extension.
|
||||
type ServicePrincipalMSISecret struct {
|
||||
msiType msiType
|
||||
clientResourceID string
|
||||
}
|
||||
|
||||
// SetAuthenticationValues is a method of the interface ServicePrincipalSecret.
|
||||
@@ -354,6 +373,7 @@ type ServicePrincipalToken struct {
|
||||
customRefreshFunc TokenRefresh
|
||||
refreshCallbacks []TokenRefreshCallback
|
||||
// MaxMSIRefreshAttempts is the maximum number of attempts to refresh an MSI token.
|
||||
// Settings this to a value less than 1 will use the default value.
|
||||
MaxMSIRefreshAttempts int
|
||||
}
|
||||
|
||||
@@ -645,78 +665,167 @@ func NewServicePrincipalTokenFromAuthorizationCode(oauthConfig OAuthConfig, clie
|
||||
)
|
||||
}
|
||||
|
||||
type msiType int
|
||||
|
||||
const (
|
||||
msiTypeUnavailable msiType = iota
|
||||
msiTypeAppServiceV20170901
|
||||
msiTypeCloudShell
|
||||
msiTypeIMDS
|
||||
)
|
||||
|
||||
func (m msiType) String() string {
|
||||
switch m {
|
||||
case msiTypeAppServiceV20170901:
|
||||
return "AppServiceV20170901"
|
||||
case msiTypeCloudShell:
|
||||
return "CloudShell"
|
||||
case msiTypeIMDS:
|
||||
return "IMDS"
|
||||
default:
|
||||
return fmt.Sprintf("unhandled MSI type %d", m)
|
||||
}
|
||||
}
|
||||
|
||||
// returns the MSI type and endpoint, or an error
|
||||
func getMSIType() (msiType, string, error) {
|
||||
if endpointEnvVar := os.Getenv(msiEndpointEnv); endpointEnvVar != "" {
|
||||
// if the env var MSI_ENDPOINT is set
|
||||
if secretEnvVar := os.Getenv(msiSecretEnv); secretEnvVar != "" {
|
||||
// if BOTH the env vars MSI_ENDPOINT and MSI_SECRET are set the msiType is AppService
|
||||
return msiTypeAppServiceV20170901, endpointEnvVar, nil
|
||||
}
|
||||
// if ONLY the env var MSI_ENDPOINT is set the msiType is CloudShell
|
||||
return msiTypeCloudShell, endpointEnvVar, nil
|
||||
}
|
||||
// if MSI_ENDPOINT is NOT set assume the msiType is IMDS
|
||||
return msiTypeIMDS, msiEndpoint, nil
|
||||
}
|
||||
|
||||
// GetMSIVMEndpoint gets the MSI endpoint on Virtual Machines.
|
||||
// NOTE: this always returns the IMDS endpoint, it does not work for app services or cloud shell.
|
||||
// Deprecated: NewServicePrincipalTokenFromMSI() and variants will automatically detect the endpoint.
|
||||
func GetMSIVMEndpoint() (string, error) {
|
||||
return msiEndpoint, nil
|
||||
}
|
||||
|
||||
func isAppService() bool {
|
||||
_, asMSIEndpointEnvExists := os.LookupEnv(asMSIEndpointEnv)
|
||||
_, asMSISecretEnvExists := os.LookupEnv(asMSISecretEnv)
|
||||
|
||||
return asMSIEndpointEnvExists && asMSISecretEnvExists
|
||||
}
|
||||
|
||||
// GetMSIAppServiceEndpoint get the MSI endpoint for App Service and Functions
|
||||
// GetMSIAppServiceEndpoint get the MSI endpoint for App Service and Functions.
|
||||
// It will return an error when not running in an app service/functions environment.
|
||||
// Deprecated: NewServicePrincipalTokenFromMSI() and variants will automatically detect the endpoint.
|
||||
func GetMSIAppServiceEndpoint() (string, error) {
|
||||
asMSIEndpoint, asMSIEndpointEnvExists := os.LookupEnv(asMSIEndpointEnv)
|
||||
|
||||
if asMSIEndpointEnvExists {
|
||||
return asMSIEndpoint, nil
|
||||
msiType, endpoint, err := getMSIType()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
switch msiType {
|
||||
case msiTypeAppServiceV20170901:
|
||||
return endpoint, nil
|
||||
default:
|
||||
return "", fmt.Errorf("%s is not app service environment", msiType)
|
||||
}
|
||||
return "", errors.New("MSI endpoint not found")
|
||||
}
|
||||
|
||||
// GetMSIEndpoint get the appropriate MSI endpoint depending on the runtime environment
|
||||
// Deprecated: NewServicePrincipalTokenFromMSI() and variants will automatically detect the endpoint.
|
||||
func GetMSIEndpoint() (string, error) {
|
||||
if isAppService() {
|
||||
return GetMSIAppServiceEndpoint()
|
||||
}
|
||||
return GetMSIVMEndpoint()
|
||||
_, endpoint, err := getMSIType()
|
||||
return endpoint, err
|
||||
}
|
||||
|
||||
// NewServicePrincipalTokenFromMSI creates a ServicePrincipalToken via the MSI VM Extension.
|
||||
// It will use the system assigned identity when creating the token.
|
||||
// msiEndpoint - empty string, or pass a non-empty string to override the default value.
|
||||
// Deprecated: use NewServicePrincipalTokenFromManagedIdentity() instead.
|
||||
func NewServicePrincipalTokenFromMSI(msiEndpoint, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, nil, callbacks...)
|
||||
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, "", "", callbacks...)
|
||||
}
|
||||
|
||||
// NewServicePrincipalTokenFromMSIWithUserAssignedID creates a ServicePrincipalToken via the MSI VM Extension.
|
||||
// It will use the specified user assigned identity when creating the token.
|
||||
// It will use the clientID of specified user assigned identity when creating the token.
|
||||
// msiEndpoint - empty string, or pass a non-empty string to override the default value.
|
||||
// Deprecated: use NewServicePrincipalTokenFromManagedIdentity() instead.
|
||||
func NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, resource string, userAssignedID string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, &userAssignedID, callbacks...)
|
||||
}
|
||||
|
||||
func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedID *string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
if err := validateStringParam(msiEndpoint, "msiEndpoint"); err != nil {
|
||||
if err := validateStringParam(userAssignedID, "userAssignedID"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, userAssignedID, "", callbacks...)
|
||||
}
|
||||
|
||||
// NewServicePrincipalTokenFromMSIWithIdentityResourceID creates a ServicePrincipalToken via the MSI VM Extension.
|
||||
// It will use the azure resource id of user assigned identity when creating the token.
|
||||
// msiEndpoint - empty string, or pass a non-empty string to override the default value.
|
||||
// Deprecated: use NewServicePrincipalTokenFromManagedIdentity() instead.
|
||||
func NewServicePrincipalTokenFromMSIWithIdentityResourceID(msiEndpoint, resource string, identityResourceID string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
if err := validateStringParam(identityResourceID, "identityResourceID"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newServicePrincipalTokenFromMSI(msiEndpoint, resource, "", identityResourceID, callbacks...)
|
||||
}
|
||||
|
||||
// ManagedIdentityOptions contains optional values for configuring managed identity authentication.
|
||||
type ManagedIdentityOptions struct {
|
||||
// ClientID is the user-assigned identity to use during authentication.
|
||||
// It is mutually exclusive with IdentityResourceID.
|
||||
ClientID string
|
||||
|
||||
// IdentityResourceID is the resource ID of the user-assigned identity to use during authentication.
|
||||
// It is mutually exclusive with ClientID.
|
||||
IdentityResourceID string
|
||||
}
|
||||
|
||||
// NewServicePrincipalTokenFromManagedIdentity creates a ServicePrincipalToken using a managed identity.
|
||||
// It supports the following managed identity environments.
|
||||
// - App Service Environment (API version 2017-09-01 only)
|
||||
// - Cloud shell
|
||||
// - IMDS with a system or user assigned identity
|
||||
func NewServicePrincipalTokenFromManagedIdentity(resource string, options *ManagedIdentityOptions, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
if options == nil {
|
||||
options = &ManagedIdentityOptions{}
|
||||
}
|
||||
return newServicePrincipalTokenFromMSI("", resource, options.ClientID, options.IdentityResourceID, callbacks...)
|
||||
}
|
||||
|
||||
func newServicePrincipalTokenFromMSI(msiEndpoint, resource, userAssignedID, identityResourceID string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) {
|
||||
if err := validateStringParam(resource, "resource"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userAssignedID != nil {
|
||||
if err := validateStringParam(*userAssignedID, "userAssignedID"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userAssignedID != "" && identityResourceID != "" {
|
||||
return nil, errors.New("cannot specify userAssignedID and identityResourceID")
|
||||
}
|
||||
// We set the oauth config token endpoint to be MSI's endpoint
|
||||
msiEndpointURL, err := url.Parse(msiEndpoint)
|
||||
msiType, endpoint, err := getMSIType()
|
||||
if err != nil {
|
||||
logger.Instance.Writef(logger.LogError, "Error determining managed identity environment: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
logger.Instance.Writef(logger.LogInfo, "Managed identity environment is %s, endpoint is %s\n", msiType, endpoint)
|
||||
if msiEndpoint != "" {
|
||||
endpoint = msiEndpoint
|
||||
logger.Instance.Writef(logger.LogInfo, "Managed identity custom endpoint is %s\n", endpoint)
|
||||
}
|
||||
msiEndpointURL, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("resource", resource)
|
||||
// App Service MSI currently only supports token API version 2017-09-01
|
||||
if isAppService() {
|
||||
v.Set("api-version", "2017-09-01")
|
||||
} else {
|
||||
v.Set("api-version", "2018-02-01")
|
||||
// cloud shell sends its data in the request body
|
||||
if msiType != msiTypeCloudShell {
|
||||
v := url.Values{}
|
||||
v.Set("resource", resource)
|
||||
clientIDParam := "client_id"
|
||||
switch msiType {
|
||||
case msiTypeAppServiceV20170901:
|
||||
clientIDParam = "clientid"
|
||||
v.Set("api-version", appServiceAPIVersion2017)
|
||||
break
|
||||
case msiTypeIMDS:
|
||||
v.Set("api-version", msiAPIVersion)
|
||||
}
|
||||
if userAssignedID != "" {
|
||||
v.Set(clientIDParam, userAssignedID)
|
||||
} else if identityResourceID != "" {
|
||||
v.Set("mi_res_id", identityResourceID)
|
||||
}
|
||||
msiEndpointURL.RawQuery = v.Encode()
|
||||
}
|
||||
if userAssignedID != nil {
|
||||
v.Set("client_id", *userAssignedID)
|
||||
}
|
||||
msiEndpointURL.RawQuery = v.Encode()
|
||||
|
||||
spt := &ServicePrincipalToken{
|
||||
inner: servicePrincipalToken{
|
||||
@@ -724,10 +833,14 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI
|
||||
OauthConfig: OAuthConfig{
|
||||
TokenEndpoint: *msiEndpointURL,
|
||||
},
|
||||
Secret: &ServicePrincipalMSISecret{},
|
||||
Secret: &ServicePrincipalMSISecret{
|
||||
msiType: msiType,
|
||||
clientResourceID: identityResourceID,
|
||||
},
|
||||
Resource: resource,
|
||||
AutoRefresh: true,
|
||||
RefreshWithin: defaultRefresh,
|
||||
ClientID: userAssignedID,
|
||||
},
|
||||
refreshLock: &sync.RWMutex{},
|
||||
sender: sender(),
|
||||
@@ -735,10 +848,6 @@ func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedI
|
||||
MaxMSIRefreshAttempts: defaultMaxMSIRefreshAttempts,
|
||||
}
|
||||
|
||||
if userAssignedID != nil {
|
||||
spt.inner.ClientID = *userAssignedID
|
||||
}
|
||||
|
||||
return spt, nil
|
||||
}
|
||||
|
||||
@@ -771,8 +880,9 @@ func (spt *ServicePrincipalToken) EnsureFresh() error {
|
||||
// EnsureFreshWithContext will refresh the token if it will expire within the refresh window (as set by
|
||||
// RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use.
|
||||
func (spt *ServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error {
|
||||
if spt.inner.AutoRefresh && spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) {
|
||||
// take the write lock then check to see if the token was already refreshed
|
||||
// must take the read lock when initially checking the token's expiration
|
||||
if spt.inner.AutoRefresh && spt.Token().WillExpireIn(spt.inner.RefreshWithin) {
|
||||
// take the write lock then check again to see if the token was already refreshed
|
||||
spt.refreshLock.Lock()
|
||||
defer spt.refreshLock.Unlock()
|
||||
if spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) {
|
||||
@@ -834,14 +944,6 @@ func (spt *ServicePrincipalToken) getGrantType() string {
|
||||
}
|
||||
}
|
||||
|
||||
func isIMDS(u url.URL) bool {
|
||||
imds, err := url.Parse(msiEndpoint)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return (u.Host == imds.Host && u.Path == imds.Path) || isAppService()
|
||||
}
|
||||
|
||||
func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource string) error {
|
||||
if spt.customRefreshFunc != nil {
|
||||
token, err := spt.customRefreshFunc(ctx, resource)
|
||||
@@ -851,19 +953,45 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
|
||||
spt.inner.Token = *token
|
||||
return spt.InvokeRefreshCallbacks(spt.inner.Token)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, spt.inner.OauthConfig.TokenEndpoint.String(), nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err)
|
||||
}
|
||||
req.Header.Add("User-Agent", UserAgent())
|
||||
// Add header when runtime is on App Service or Functions
|
||||
if isAppService() {
|
||||
asMSISecret, _ := os.LookupEnv(asMSISecretEnv)
|
||||
req.Header.Add("Secret", asMSISecret)
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
if !isIMDS(spt.inner.OauthConfig.TokenEndpoint) {
|
||||
var resp *http.Response
|
||||
authBodyFilter := func(b []byte) []byte {
|
||||
if logger.Level() != logger.LogAuth {
|
||||
return []byte("**REDACTED** authentication body")
|
||||
}
|
||||
return b
|
||||
}
|
||||
if msiSecret, ok := spt.inner.Secret.(*ServicePrincipalMSISecret); ok {
|
||||
switch msiSecret.msiType {
|
||||
case msiTypeAppServiceV20170901:
|
||||
req.Method = http.MethodGet
|
||||
req.Header.Set("secret", os.Getenv(msiSecretEnv))
|
||||
break
|
||||
case msiTypeCloudShell:
|
||||
req.Header.Set("Metadata", "true")
|
||||
data := url.Values{}
|
||||
data.Set("resource", spt.inner.Resource)
|
||||
if spt.inner.ClientID != "" {
|
||||
data.Set("client_id", spt.inner.ClientID)
|
||||
} else if msiSecret.clientResourceID != "" {
|
||||
data.Set("msi_res_id", msiSecret.clientResourceID)
|
||||
}
|
||||
req.Body = ioutil.NopCloser(strings.NewReader(data.Encode()))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
break
|
||||
case msiTypeIMDS:
|
||||
req.Method = http.MethodGet
|
||||
req.Header.Set("Metadata", "true")
|
||||
break
|
||||
}
|
||||
logger.Instance.WriteRequest(req, logger.Filter{Body: authBodyFilter})
|
||||
resp, err = retryForIMDS(spt.sender, req, spt.MaxMSIRefreshAttempts)
|
||||
} else {
|
||||
v := url.Values{}
|
||||
v.Set("client_id", spt.inner.ClientID)
|
||||
v.Set("resource", resource)
|
||||
@@ -892,32 +1020,26 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
|
||||
req.ContentLength = int64(len(s))
|
||||
req.Header.Set(contentType, mimeTypeFormPost)
|
||||
req.Body = body
|
||||
}
|
||||
|
||||
if _, ok := spt.inner.Secret.(*ServicePrincipalMSISecret); ok {
|
||||
req.Method = http.MethodGet
|
||||
req.Header.Set(metadataHeader, "true")
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
if isIMDS(spt.inner.OauthConfig.TokenEndpoint) {
|
||||
resp, err = retryForIMDS(spt.sender, req, spt.MaxMSIRefreshAttempts)
|
||||
} else {
|
||||
logger.Instance.WriteRequest(req, logger.Filter{Body: authBodyFilter})
|
||||
resp, err = spt.sender.Do(req)
|
||||
}
|
||||
|
||||
// don't return a TokenRefreshError here; this will allow retry logic to apply
|
||||
if err != nil {
|
||||
// don't return a TokenRefreshError here; this will allow retry logic to apply
|
||||
return fmt.Errorf("adal: Failed to execute the refresh request. Error = '%v'", err)
|
||||
} else if resp == nil {
|
||||
return fmt.Errorf("adal: received nil response and error")
|
||||
}
|
||||
|
||||
logger.Instance.WriteResponse(resp, logger.Filter{Body: authBodyFilter})
|
||||
defer resp.Body.Close()
|
||||
rb, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if err != nil {
|
||||
return newTokenRefreshError(fmt.Sprintf("adal: Refresh request failed. Status Code = '%d'. Failed reading response body: %v", resp.StatusCode, err), resp)
|
||||
return newTokenRefreshError(fmt.Sprintf("adal: Refresh request failed. Status Code = '%d'. Failed reading response body: %v Endpoint %s", resp.StatusCode, err, req.URL.String()), resp)
|
||||
}
|
||||
return newTokenRefreshError(fmt.Sprintf("adal: Refresh request failed. Status Code = '%d'. Response body: %s", resp.StatusCode, string(rb)), resp)
|
||||
return newTokenRefreshError(fmt.Sprintf("adal: Refresh request failed. Status Code = '%d'. Response body: %s Endpoint %s", resp.StatusCode, string(rb), req.URL.String()), resp)
|
||||
}
|
||||
|
||||
// for the following error cases don't return a TokenRefreshError. the operation succeeded
|
||||
@@ -930,15 +1052,60 @@ func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource
|
||||
if len(strings.Trim(string(rb), " ")) == 0 {
|
||||
return fmt.Errorf("adal: Empty service principal token received during refresh")
|
||||
}
|
||||
var token Token
|
||||
token := struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
|
||||
// AAD returns expires_in as a string, ADFS returns it as an int
|
||||
ExpiresIn json.Number `json:"expires_in"`
|
||||
// expires_on can be in two formats, a UTC time stamp or the number of seconds.
|
||||
ExpiresOn string `json:"expires_on"`
|
||||
NotBefore json.Number `json:"not_before"`
|
||||
|
||||
Resource string `json:"resource"`
|
||||
Type string `json:"token_type"`
|
||||
}{}
|
||||
// return a TokenRefreshError in the follow error cases as the token is in an unexpected format
|
||||
err = json.Unmarshal(rb, &token)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adal: Failed to unmarshal the service principal token during refresh. Error = '%v' JSON = '%s'", err, string(rb))
|
||||
return newTokenRefreshError(fmt.Sprintf("adal: Failed to unmarshal the service principal token during refresh. Error = '%v' JSON = '%s'", err, string(rb)), resp)
|
||||
}
|
||||
expiresOn := json.Number("")
|
||||
// ADFS doesn't include the expires_on field
|
||||
if token.ExpiresOn != "" {
|
||||
if expiresOn, err = parseExpiresOn(token.ExpiresOn); err != nil {
|
||||
return newTokenRefreshError(fmt.Sprintf("adal: failed to parse expires_on: %v value '%s'", err, token.ExpiresOn), resp)
|
||||
}
|
||||
}
|
||||
spt.inner.Token.AccessToken = token.AccessToken
|
||||
spt.inner.Token.RefreshToken = token.RefreshToken
|
||||
spt.inner.Token.ExpiresIn = token.ExpiresIn
|
||||
spt.inner.Token.ExpiresOn = expiresOn
|
||||
spt.inner.Token.NotBefore = token.NotBefore
|
||||
spt.inner.Token.Resource = token.Resource
|
||||
spt.inner.Token.Type = token.Type
|
||||
|
||||
spt.inner.Token = token
|
||||
return spt.InvokeRefreshCallbacks(spt.inner.Token)
|
||||
}
|
||||
|
||||
return spt.InvokeRefreshCallbacks(token)
|
||||
// converts expires_on to the number of seconds
|
||||
func parseExpiresOn(s string) (json.Number, error) {
|
||||
// convert the expiration date to the number of seconds from now
|
||||
timeToDuration := func(t time.Time) json.Number {
|
||||
dur := t.Sub(time.Now().UTC())
|
||||
return json.Number(strconv.FormatInt(int64(dur.Round(time.Second).Seconds()), 10))
|
||||
}
|
||||
if _, err := strconv.ParseInt(s, 10, 64); err == nil {
|
||||
// this is the number of seconds case, no conversion required
|
||||
return json.Number(s), nil
|
||||
} else if eo, err := time.Parse(expiresOnDateFormatPM, s); err == nil {
|
||||
return timeToDuration(eo), nil
|
||||
} else if eo, err := time.Parse(expiresOnDateFormat, s); err == nil {
|
||||
return timeToDuration(eo), nil
|
||||
} else {
|
||||
// unknown format
|
||||
return json.Number(""), err
|
||||
}
|
||||
}
|
||||
|
||||
// retry logic specific to retrieving a token from the IMDS endpoint
|
||||
@@ -972,6 +1139,11 @@ func retryForIMDS(sender Sender, req *http.Request, maxAttempts int) (resp *http
|
||||
attempt := 0
|
||||
delay := time.Duration(0)
|
||||
|
||||
// maxAttempts is user-specified, ensure that its value is greater than zero else no request will be made
|
||||
if maxAttempts < 1 {
|
||||
maxAttempts = defaultMaxMSIRefreshAttempts
|
||||
}
|
||||
|
||||
for attempt < maxAttempts {
|
||||
if resp != nil && resp.Body != nil {
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
@@ -1064,46 +1236,6 @@ func (mt *MultiTenantServicePrincipalToken) AuxiliaryOAuthTokens() []string {
|
||||
return tokens
|
||||
}
|
||||
|
||||
// EnsureFreshWithContext will refresh the token if it will expire within the refresh window (as set by
|
||||
// RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use.
|
||||
func (mt *MultiTenantServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error {
|
||||
if err := mt.PrimaryToken.EnsureFreshWithContext(ctx); err != nil {
|
||||
return fmt.Errorf("failed to refresh primary token: %v", err)
|
||||
}
|
||||
for _, aux := range mt.AuxiliaryTokens {
|
||||
if err := aux.EnsureFreshWithContext(ctx); err != nil {
|
||||
return fmt.Errorf("failed to refresh auxiliary token: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RefreshWithContext obtains a fresh token for the Service Principal.
|
||||
func (mt *MultiTenantServicePrincipalToken) RefreshWithContext(ctx context.Context) error {
|
||||
if err := mt.PrimaryToken.RefreshWithContext(ctx); err != nil {
|
||||
return fmt.Errorf("failed to refresh primary token: %v", err)
|
||||
}
|
||||
for _, aux := range mt.AuxiliaryTokens {
|
||||
if err := aux.RefreshWithContext(ctx); err != nil {
|
||||
return fmt.Errorf("failed to refresh auxiliary token: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RefreshExchangeWithContext refreshes the token, but for a different resource.
|
||||
func (mt *MultiTenantServicePrincipalToken) RefreshExchangeWithContext(ctx context.Context, resource string) error {
|
||||
if err := mt.PrimaryToken.RefreshExchangeWithContext(ctx, resource); err != nil {
|
||||
return fmt.Errorf("failed to refresh primary token: %v", err)
|
||||
}
|
||||
for _, aux := range mt.AuxiliaryTokens {
|
||||
if err := aux.RefreshExchangeWithContext(ctx, resource); err != nil {
|
||||
return fmt.Errorf("failed to refresh auxiliary token: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewMultiTenantServicePrincipalToken creates a new MultiTenantServicePrincipalToken with the specified credentials and resource.
|
||||
func NewMultiTenantServicePrincipalToken(multiTenantCfg MultiTenantOAuthConfig, clientID string, secret string, resource string) (*MultiTenantServicePrincipalToken, error) {
|
||||
if err := validateStringParam(clientID, "clientID"); err != nil {
|
||||
@@ -1133,3 +1265,64 @@ func NewMultiTenantServicePrincipalToken(multiTenantCfg MultiTenantOAuthConfig,
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
// NewMultiTenantServicePrincipalTokenFromCertificate creates a new MultiTenantServicePrincipalToken with the specified certificate credentials and resource.
|
||||
func NewMultiTenantServicePrincipalTokenFromCertificate(multiTenantCfg MultiTenantOAuthConfig, clientID string, certificate *x509.Certificate, privateKey *rsa.PrivateKey, resource string) (*MultiTenantServicePrincipalToken, error) {
|
||||
if err := validateStringParam(clientID, "clientID"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateStringParam(resource, "resource"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if certificate == nil {
|
||||
return nil, fmt.Errorf("parameter 'certificate' cannot be nil")
|
||||
}
|
||||
if privateKey == nil {
|
||||
return nil, fmt.Errorf("parameter 'privateKey' cannot be nil")
|
||||
}
|
||||
auxTenants := multiTenantCfg.AuxiliaryTenants()
|
||||
m := MultiTenantServicePrincipalToken{
|
||||
AuxiliaryTokens: make([]*ServicePrincipalToken, len(auxTenants)),
|
||||
}
|
||||
primary, err := NewServicePrincipalTokenWithSecret(
|
||||
*multiTenantCfg.PrimaryTenant(),
|
||||
clientID,
|
||||
resource,
|
||||
&ServicePrincipalCertificateSecret{
|
||||
PrivateKey: privateKey,
|
||||
Certificate: certificate,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create SPT for primary tenant: %v", err)
|
||||
}
|
||||
m.PrimaryToken = primary
|
||||
for i := range auxTenants {
|
||||
aux, err := NewServicePrincipalTokenWithSecret(
|
||||
*auxTenants[i],
|
||||
clientID,
|
||||
resource,
|
||||
&ServicePrincipalCertificateSecret{
|
||||
PrivateKey: privateKey,
|
||||
Certificate: certificate,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create SPT for auxiliary tenant: %v", err)
|
||||
}
|
||||
m.AuxiliaryTokens[i] = aux
|
||||
}
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
// MSIAvailable returns true if the MSI endpoint is available for authentication.
|
||||
func MSIAvailable(ctx context.Context, s Sender) bool {
|
||||
if s == nil {
|
||||
s = sender()
|
||||
}
|
||||
resp, err := getMSIEndpoint(ctx, s)
|
||||
if err == nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
|
||||
76
vendor/github.com/Azure/go-autorest/autorest/adal/token_1.13.go
generated
vendored
Normal file
76
vendor/github.com/Azure/go-autorest/autorest/adal/token_1.13.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
//go:build go1.13
|
||||
// +build go1.13
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// 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 adal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getMSIEndpoint(ctx context.Context, sender Sender) (*http.Response, error) {
|
||||
tempCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||
defer cancel()
|
||||
// http.NewRequestWithContext() was added in Go 1.13
|
||||
req, _ := http.NewRequestWithContext(tempCtx, http.MethodGet, msiEndpoint, nil)
|
||||
q := req.URL.Query()
|
||||
q.Add("api-version", msiAPIVersion)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
return sender.Do(req)
|
||||
}
|
||||
|
||||
// EnsureFreshWithContext will refresh the token if it will expire within the refresh window (as set by
|
||||
// RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use.
|
||||
func (mt *MultiTenantServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error {
|
||||
if err := mt.PrimaryToken.EnsureFreshWithContext(ctx); err != nil {
|
||||
return fmt.Errorf("failed to refresh primary token: %w", err)
|
||||
}
|
||||
for _, aux := range mt.AuxiliaryTokens {
|
||||
if err := aux.EnsureFreshWithContext(ctx); err != nil {
|
||||
return fmt.Errorf("failed to refresh auxiliary token: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RefreshWithContext obtains a fresh token for the Service Principal.
|
||||
func (mt *MultiTenantServicePrincipalToken) RefreshWithContext(ctx context.Context) error {
|
||||
if err := mt.PrimaryToken.RefreshWithContext(ctx); err != nil {
|
||||
return fmt.Errorf("failed to refresh primary token: %w", err)
|
||||
}
|
||||
for _, aux := range mt.AuxiliaryTokens {
|
||||
if err := aux.RefreshWithContext(ctx); err != nil {
|
||||
return fmt.Errorf("failed to refresh auxiliary token: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RefreshExchangeWithContext refreshes the token, but for a different resource.
|
||||
func (mt *MultiTenantServicePrincipalToken) RefreshExchangeWithContext(ctx context.Context, resource string) error {
|
||||
if err := mt.PrimaryToken.RefreshExchangeWithContext(ctx, resource); err != nil {
|
||||
return fmt.Errorf("failed to refresh primary token: %w", err)
|
||||
}
|
||||
for _, aux := range mt.AuxiliaryTokens {
|
||||
if err := aux.RefreshExchangeWithContext(ctx, resource); err != nil {
|
||||
return fmt.Errorf("failed to refresh auxiliary token: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
75
vendor/github.com/Azure/go-autorest/autorest/adal/token_legacy.go
generated
vendored
Normal file
75
vendor/github.com/Azure/go-autorest/autorest/adal/token_legacy.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
//go:build !go1.13
|
||||
// +build !go1.13
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// 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 adal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getMSIEndpoint(ctx context.Context, sender Sender) (*http.Response, error) {
|
||||
tempCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||
defer cancel()
|
||||
req, _ := http.NewRequest(http.MethodGet, msiEndpoint, nil)
|
||||
req = req.WithContext(tempCtx)
|
||||
q := req.URL.Query()
|
||||
q.Add("api-version", msiAPIVersion)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
return sender.Do(req)
|
||||
}
|
||||
|
||||
// EnsureFreshWithContext will refresh the token if it will expire within the refresh window (as set by
|
||||
// RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use.
|
||||
func (mt *MultiTenantServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error {
|
||||
if err := mt.PrimaryToken.EnsureFreshWithContext(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, aux := range mt.AuxiliaryTokens {
|
||||
if err := aux.EnsureFreshWithContext(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RefreshWithContext obtains a fresh token for the Service Principal.
|
||||
func (mt *MultiTenantServicePrincipalToken) RefreshWithContext(ctx context.Context) error {
|
||||
if err := mt.PrimaryToken.RefreshWithContext(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, aux := range mt.AuxiliaryTokens {
|
||||
if err := aux.RefreshWithContext(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RefreshExchangeWithContext refreshes the token, but for a different resource.
|
||||
func (mt *MultiTenantServicePrincipalToken) RefreshExchangeWithContext(ctx context.Context, resource string) error {
|
||||
if err := mt.PrimaryToken.RefreshExchangeWithContext(ctx, resource); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, aux := range mt.AuxiliaryTokens {
|
||||
if err := aux.RefreshExchangeWithContext(ctx, resource); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
24
vendor/github.com/Azure/go-autorest/autorest/authorization.go
generated
vendored
24
vendor/github.com/Azure/go-autorest/autorest/authorization.go
generated
vendored
@@ -138,6 +138,11 @@ func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
}
|
||||
}
|
||||
|
||||
// TokenProvider returns OAuthTokenProvider so that it can be used for authorization outside the REST.
|
||||
func (ba *BearerAuthorizer) TokenProvider() adal.OAuthTokenProvider {
|
||||
return ba.tokenProvider
|
||||
}
|
||||
|
||||
// BearerAuthorizerCallbackFunc is the authentication callback signature.
|
||||
type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error)
|
||||
|
||||
@@ -294,18 +299,24 @@ type MultiTenantServicePrincipalTokenAuthorizer interface {
|
||||
|
||||
// NewMultiTenantServicePrincipalTokenAuthorizer crates a BearerAuthorizer using the given token provider
|
||||
func NewMultiTenantServicePrincipalTokenAuthorizer(tp adal.MultitenantOAuthTokenProvider) MultiTenantServicePrincipalTokenAuthorizer {
|
||||
return &multiTenantSPTAuthorizer{tp: tp}
|
||||
return NewMultiTenantBearerAuthorizer(tp)
|
||||
}
|
||||
|
||||
type multiTenantSPTAuthorizer struct {
|
||||
// MultiTenantBearerAuthorizer implements bearer authorization across multiple tenants.
|
||||
type MultiTenantBearerAuthorizer struct {
|
||||
tp adal.MultitenantOAuthTokenProvider
|
||||
}
|
||||
|
||||
// NewMultiTenantBearerAuthorizer creates a MultiTenantBearerAuthorizer using the given token provider.
|
||||
func NewMultiTenantBearerAuthorizer(tp adal.MultitenantOAuthTokenProvider) *MultiTenantBearerAuthorizer {
|
||||
return &MultiTenantBearerAuthorizer{tp: tp}
|
||||
}
|
||||
|
||||
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header using the
|
||||
// primary token along with the auxiliary authorization header using the auxiliary tokens.
|
||||
//
|
||||
// By default, the token will be automatically refreshed through the Refresher interface.
|
||||
func (mt multiTenantSPTAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
func (mt *MultiTenantBearerAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
@@ -331,7 +342,12 @@ func (mt multiTenantSPTAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
for i := range auxTokens {
|
||||
auxTokens[i] = fmt.Sprintf("Bearer %s", auxTokens[i])
|
||||
}
|
||||
return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, "; ")))
|
||||
return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, ", ")))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TokenProvider returns the underlying MultitenantOAuthTokenProvider for this authorizer.
|
||||
func (mt *MultiTenantBearerAuthorizer) TokenProvider() adal.MultitenantOAuthTokenProvider {
|
||||
return mt.tp
|
||||
}
|
||||
|
||||
7
vendor/github.com/Azure/go-autorest/autorest/authorization_sas.go
generated
vendored
7
vendor/github.com/Azure/go-autorest/autorest/authorization_sas.go
generated
vendored
@@ -54,13 +54,12 @@ func (sas *SASTokenAuthorizer) WithAuthorization() PrepareDecorator {
|
||||
return r, err
|
||||
}
|
||||
|
||||
if r.URL.RawQuery != "" {
|
||||
r.URL.RawQuery = fmt.Sprintf("%s&%s", r.URL.RawQuery, sas.sasToken)
|
||||
} else {
|
||||
if r.URL.RawQuery == "" {
|
||||
r.URL.RawQuery = sas.sasToken
|
||||
} else if !strings.Contains(r.URL.RawQuery, sas.sasToken) {
|
||||
r.URL.RawQuery = fmt.Sprintf("%s&%s", r.URL.RawQuery, sas.sasToken)
|
||||
}
|
||||
|
||||
r.RequestURI = r.URL.String()
|
||||
return Prepare(r)
|
||||
})
|
||||
}
|
||||
|
||||
3
vendor/github.com/Azure/go-autorest/autorest/authorization_storage.go
generated
vendored
3
vendor/github.com/Azure/go-autorest/autorest/authorization_storage.go
generated
vendored
@@ -152,6 +152,9 @@ func buildCanonicalizedResource(accountName, uri string, keyType SharedKeyType)
|
||||
// the resource's URI should be encoded exactly as it is in the URI.
|
||||
// -- https://msdn.microsoft.com/en-gb/library/azure/dd179428.aspx
|
||||
cr.WriteString(u.EscapedPath())
|
||||
} else {
|
||||
// a slash is required to indicate the root path
|
||||
cr.WriteString("/")
|
||||
}
|
||||
|
||||
params, err := url.ParseQuery(u.RawQuery)
|
||||
|
||||
69
vendor/github.com/Azure/go-autorest/autorest/azure/async.go
generated
vendored
69
vendor/github.com/Azure/go-autorest/autorest/azure/async.go
generated
vendored
@@ -26,6 +26,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/logger"
|
||||
"github.com/Azure/go-autorest/tracing"
|
||||
)
|
||||
|
||||
@@ -42,6 +43,52 @@ const (
|
||||
|
||||
var pollingCodes = [...]int{http.StatusNoContent, http.StatusAccepted, http.StatusCreated, http.StatusOK}
|
||||
|
||||
// FutureAPI contains the set of methods on the Future type.
|
||||
type FutureAPI interface {
|
||||
// Response returns the last HTTP response.
|
||||
Response() *http.Response
|
||||
|
||||
// Status returns the last status message of the operation.
|
||||
Status() string
|
||||
|
||||
// PollingMethod returns the method used to monitor the status of the asynchronous operation.
|
||||
PollingMethod() PollingMethodType
|
||||
|
||||
// DoneWithContext queries the service to see if the operation has completed.
|
||||
DoneWithContext(context.Context, autorest.Sender) (bool, error)
|
||||
|
||||
// GetPollingDelay returns a duration the application should wait before checking
|
||||
// the status of the asynchronous request and true; this value is returned from
|
||||
// the service via the Retry-After response header. If the header wasn't returned
|
||||
// then the function returns the zero-value time.Duration and false.
|
||||
GetPollingDelay() (time.Duration, bool)
|
||||
|
||||
// WaitForCompletionRef will return when one of the following conditions is met: the long
|
||||
// running operation has completed, the provided context is cancelled, or the client's
|
||||
// polling duration has been exceeded. It will retry failed polling attempts based on
|
||||
// the retry value defined in the client up to the maximum retry attempts.
|
||||
// If no deadline is specified in the context then the client.PollingDuration will be
|
||||
// used to determine if a default deadline should be used.
|
||||
// If PollingDuration is greater than zero the value will be used as the context's timeout.
|
||||
// If PollingDuration is zero then no default deadline will be used.
|
||||
WaitForCompletionRef(context.Context, autorest.Client) error
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
MarshalJSON() ([]byte, error)
|
||||
|
||||
// MarshalJSON implements the json.Unmarshaler interface.
|
||||
UnmarshalJSON([]byte) error
|
||||
|
||||
// PollingURL returns the URL used for retrieving the status of the long-running operation.
|
||||
PollingURL() string
|
||||
|
||||
// GetResult should be called once polling has completed successfully.
|
||||
// It makes the final GET call to retrieve the resultant payload.
|
||||
GetResult(autorest.Sender) (*http.Response, error)
|
||||
}
|
||||
|
||||
var _ FutureAPI = (*Future)(nil)
|
||||
|
||||
// Future provides a mechanism to access the status and results of an asynchronous request.
|
||||
// Since futures are stateful they should be passed by value to avoid race conditions.
|
||||
type Future struct {
|
||||
@@ -167,7 +214,14 @@ func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Clien
|
||||
cancelCtx, cancel = context.WithTimeout(ctx, d)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// if the initial response has a Retry-After, sleep for the specified amount of time before starting to poll
|
||||
if delay, ok := f.GetPollingDelay(); ok {
|
||||
logger.Instance.Writeln(logger.LogInfo, "WaitForCompletionRef: initial polling delay")
|
||||
if delayElapsed := autorest.DelayForBackoff(delay, 0, cancelCtx.Done()); !delayElapsed {
|
||||
err = cancelCtx.Err()
|
||||
return
|
||||
}
|
||||
}
|
||||
done, err := f.DoneWithContext(ctx, client)
|
||||
for attempts := 0; !done; done, err = f.DoneWithContext(ctx, client) {
|
||||
if attempts >= client.RetryAttempts {
|
||||
@@ -182,12 +236,14 @@ func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Clien
|
||||
var ok bool
|
||||
delay, ok = f.GetPollingDelay()
|
||||
if !ok {
|
||||
logger.Instance.Writeln(logger.LogInfo, "WaitForCompletionRef: Using client polling delay")
|
||||
delay = client.PollingDelay
|
||||
}
|
||||
} else {
|
||||
// there was an error polling for status so perform exponential
|
||||
// back-off based on the number of attempts using the client's retry
|
||||
// duration. update attempts after delayAttempt to avoid off-by-one.
|
||||
logger.Instance.Writef(logger.LogError, "WaitForCompletionRef: %s\n", err)
|
||||
delayAttempt = attempts
|
||||
delay = client.RetryDuration
|
||||
attempts++
|
||||
@@ -407,12 +463,12 @@ func (pt *pollingTrackerBase) updateRawBody() error {
|
||||
if err != nil {
|
||||
return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to read response body")
|
||||
}
|
||||
// put the body back so it's available to other callers
|
||||
pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b))
|
||||
// observed in 204 responses over HTTP/2.0; the content length is -1 but body is empty
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
// put the body back so it's available to other callers
|
||||
pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b))
|
||||
if err = json.Unmarshal(b, &pt.rawBody); err != nil {
|
||||
return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to unmarshal response body")
|
||||
}
|
||||
@@ -460,7 +516,12 @@ func (pt *pollingTrackerBase) updateErrorFromResponse() {
|
||||
re := respErr{}
|
||||
defer pt.resp.Body.Close()
|
||||
var b []byte
|
||||
if b, err = ioutil.ReadAll(pt.resp.Body); err != nil || len(b) == 0 {
|
||||
if b, err = ioutil.ReadAll(pt.resp.Body); err != nil {
|
||||
goto Default
|
||||
}
|
||||
// put the body back so it's available to other callers
|
||||
pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b))
|
||||
if len(b) == 0 {
|
||||
goto Default
|
||||
}
|
||||
if err = json.Unmarshal(b, &re); err != nil {
|
||||
|
||||
152
vendor/github.com/Azure/go-autorest/autorest/azure/auth/README.md
generated
vendored
Normal file
152
vendor/github.com/Azure/go-autorest/autorest/azure/auth/README.md
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
# NOTE: This module will go out of support by March 31, 2023. For authenticating with Azure AD, use module [azidentity](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) instead. For help migrating from `auth` to `azidentiy` please consult the [migration guide](https://aka.ms/azsdk/go/identity/migration). General information about the retirement of this and other legacy modules can be found [here](https://azure.microsoft.com/updates/support-for-azure-sdk-libraries-that-do-not-conform-to-our-current-azure-sdk-guidelines-will-be-retired-as-of-31-march-2023/).
|
||||
|
||||
## Authentication
|
||||
|
||||
Typical SDK operations must be authenticated and authorized. The `autorest.Authorizer`
|
||||
interface allows use of any auth style in requests, such as inserting an OAuth2
|
||||
Authorization header and bearer token received from Azure AD.
|
||||
|
||||
The SDK itself provides a simple way to get an authorizer which first checks
|
||||
for OAuth client credentials in environment variables and then falls back to
|
||||
Azure's [Managed Service Identity]() when available, e.g. when on an Azure
|
||||
VM. The following snippet from [the previous section](#use) demonstrates
|
||||
this helper.
|
||||
|
||||
```go
|
||||
import "github.com/Azure/go-autorest/autorest/azure/auth"
|
||||
|
||||
// create a VirtualNetworks client
|
||||
vnetClient := network.NewVirtualNetworksClient("<subscriptionID>")
|
||||
|
||||
// create an authorizer from env vars or Azure Managed Service Idenity
|
||||
authorizer, err := auth.NewAuthorizerFromEnvironment()
|
||||
if err != nil {
|
||||
handle(err)
|
||||
}
|
||||
|
||||
vnetClient.Authorizer = authorizer
|
||||
|
||||
// call the VirtualNetworks CreateOrUpdate API
|
||||
vnetClient.CreateOrUpdate(context.Background(),
|
||||
// ...
|
||||
```
|
||||
|
||||
The following environment variables help determine authentication configuration:
|
||||
|
||||
- `AZURE_ENVIRONMENT`: Specifies the Azure Environment to use. If not set, it
|
||||
defaults to `AzurePublicCloud`. Not applicable to authentication with Managed
|
||||
Service Identity (MSI).
|
||||
- `AZURE_AD_RESOURCE`: Specifies the AAD resource ID to use. If not set, it
|
||||
defaults to `ResourceManagerEndpoint` for operations with Azure Resource
|
||||
Manager. You can also choose an alternate resource programmatically with
|
||||
`auth.NewAuthorizerFromEnvironmentWithResource(resource string)`.
|
||||
|
||||
### More Authentication Details
|
||||
|
||||
The previous is the first and most recommended of several authentication
|
||||
options offered by the SDK because it allows seamless use of both service
|
||||
principals and [Azure Managed Service Identity][]. Other options are listed
|
||||
below.
|
||||
|
||||
> Note: If you need to create a new service principal, run `az ad sp create-for-rbac -n "<app_name>"` in the
|
||||
> [azure-cli](https://github.com/Azure/azure-cli). See [these
|
||||
> docs](https://docs.microsoft.com/cli/azure/create-an-azure-service-principal-azure-cli?view=azure-cli-latest)
|
||||
> for more info. Copy the new principal's ID, secret, and tenant ID for use in
|
||||
> your app, or consider the `--sdk-auth` parameter for serialized output.
|
||||
|
||||
[azure managed service identity]: https://docs.microsoft.com/azure/active-directory/msi-overview
|
||||
|
||||
- The `auth.NewAuthorizerFromEnvironment()` described above creates an authorizer
|
||||
from the first available of the following configuration:
|
||||
|
||||
1. **Client Credentials**: Azure AD Application ID and Secret.
|
||||
|
||||
- `AZURE_TENANT_ID`: Specifies the Tenant to which to authenticate.
|
||||
- `AZURE_CLIENT_ID`: Specifies the app client ID to use.
|
||||
- `AZURE_CLIENT_SECRET`: Specifies the app secret to use.
|
||||
|
||||
2. **Client Certificate**: Azure AD Application ID and X.509 Certificate.
|
||||
|
||||
- `AZURE_TENANT_ID`: Specifies the Tenant to which to authenticate.
|
||||
- `AZURE_CLIENT_ID`: Specifies the app client ID to use.
|
||||
- `AZURE_CERTIFICATE_PATH`: Specifies the certificate Path to use.
|
||||
- `AZURE_CERTIFICATE_PASSWORD`: Specifies the certificate password to use.
|
||||
|
||||
3. **Resource Owner Password**: Azure AD User and Password. This grant type is *not
|
||||
recommended*, use device login instead if you need interactive login.
|
||||
|
||||
- `AZURE_TENANT_ID`: Specifies the Tenant to which to authenticate.
|
||||
- `AZURE_CLIENT_ID`: Specifies the app client ID to use.
|
||||
- `AZURE_USERNAME`: Specifies the username to use.
|
||||
- `AZURE_PASSWORD`: Specifies the password to use.
|
||||
|
||||
4. **Azure Managed Service Identity**: Delegate credential management to the
|
||||
platform. Requires that code is running in Azure, e.g. on a VM. All
|
||||
configuration is handled by Azure. See [Azure Managed Service
|
||||
Identity](https://docs.microsoft.com/azure/active-directory/msi-overview)
|
||||
for more details.
|
||||
|
||||
- The `auth.NewAuthorizerFromFile()` method creates an authorizer using
|
||||
credentials from an auth file created by the [Azure CLI][]. Follow these
|
||||
steps to utilize:
|
||||
|
||||
1. Create a service principal and output an auth file using `az ad sp create-for-rbac --sdk-auth > client_credentials.json`.
|
||||
2. Set environment variable `AZURE_AUTH_LOCATION` to the path of the saved
|
||||
output file.
|
||||
3. Use the authorizer returned by `auth.NewAuthorizerFromFile()` in your
|
||||
client as described above.
|
||||
|
||||
- The `auth.NewAuthorizerFromCLI()` method creates an authorizer which
|
||||
uses [Azure CLI][] to obtain its credentials.
|
||||
|
||||
The default audience being requested is `https://management.azure.com` (Azure ARM API).
|
||||
To specify your own audience, export `AZURE_AD_RESOURCE` as an evironment variable.
|
||||
This is read by `auth.NewAuthorizerFromCLI()` and passed to Azure CLI to acquire the access token.
|
||||
|
||||
For example, to request an access token for Azure Key Vault, export
|
||||
```
|
||||
AZURE_AD_RESOURCE="https://vault.azure.net"
|
||||
```
|
||||
|
||||
- `auth.NewAuthorizerFromCLIWithResource(AUDIENCE_URL_OR_APPLICATION_ID)` - this method is self contained and does
|
||||
not require exporting environment variables. For example, to request an access token for Azure Key Vault:
|
||||
```
|
||||
auth.NewAuthorizerFromCLIWithResource("https://vault.azure.net")
|
||||
```
|
||||
|
||||
To use `NewAuthorizerFromCLI()` or `NewAuthorizerFromCLIWithResource()`, follow these steps:
|
||||
|
||||
1. Install [Azure CLI v2.0.12](https://docs.microsoft.com/cli/azure/install-azure-cli) or later. Upgrade earlier versions.
|
||||
2. Use `az login` to sign in to Azure.
|
||||
|
||||
If you receive an error, use `az account get-access-token` to verify access.
|
||||
|
||||
If Azure CLI is not installed to the default directory, you may receive an error
|
||||
reporting that `az` cannot be found.
|
||||
Use the `AzureCLIPath` environment variable to define the Azure CLI installation folder.
|
||||
|
||||
If you are signed in to Azure CLI using multiple accounts or your account has
|
||||
access to multiple subscriptions, you need to specify the specific subscription
|
||||
to be used. To do so, use:
|
||||
|
||||
```
|
||||
az account set --subscription <subscription-id>
|
||||
```
|
||||
|
||||
To verify the current account settings, use:
|
||||
|
||||
```
|
||||
az account list
|
||||
```
|
||||
|
||||
[azure cli]: https://github.com/Azure/azure-cli
|
||||
|
||||
- Finally, you can use OAuth's [Device Flow][] by calling
|
||||
`auth.NewDeviceFlowConfig()` and extracting the Authorizer as follows:
|
||||
|
||||
```go
|
||||
config := auth.NewDeviceFlowConfig(clientID, tenantID)
|
||||
a, err := config.Authorizer()
|
||||
```
|
||||
|
||||
[device flow]: https://oauth.net/2/device-flow/
|
||||
117
vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go
generated
vendored
117
vendor/github.com/Azure/go-autorest/autorest/azure/auth/auth.go
generated
vendored
@@ -16,8 +16,7 @@ package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -32,8 +31,8 @@ import (
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/Azure/go-autorest/autorest/azure/cli"
|
||||
"github.com/Azure/go-autorest/logger"
|
||||
"github.com/dimchansky/utfbom"
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
)
|
||||
|
||||
// The possible keys in the Values map.
|
||||
@@ -63,6 +62,7 @@ const (
|
||||
// 3. Username password
|
||||
// 4. MSI
|
||||
func NewAuthorizerFromEnvironment() (autorest.Authorizer, error) {
|
||||
logger.Instance.Writeln(logger.LogInfo, "NewAuthorizerFromEnvironment() determining authentication mechanism")
|
||||
settings, err := GetSettingsFromEnvironment()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -76,6 +76,7 @@ func NewAuthorizerFromEnvironment() (autorest.Authorizer, error) {
|
||||
// 3. Username password
|
||||
// 4. MSI
|
||||
func NewAuthorizerFromEnvironmentWithResource(resource string) (autorest.Authorizer, error) {
|
||||
logger.Instance.Writeln(logger.LogInfo, "NewAuthorizerFromEnvironmentWithResource() determining authentication mechanism")
|
||||
settings, err := GetSettingsFromEnvironment()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -125,6 +126,7 @@ func (settings EnvironmentSettings) GetSubscriptionID() string {
|
||||
// adds the specified environment variable value to the Values map if it exists
|
||||
func (settings EnvironmentSettings) setValue(key string) {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
logger.Instance.Writef(logger.LogInfo, "GetSettingsFromEnvironment() found environment var %s\n", key)
|
||||
settings.Values[key] = v
|
||||
}
|
||||
}
|
||||
@@ -141,6 +143,7 @@ func (settings EnvironmentSettings) getClientAndTenant() (string, string) {
|
||||
func (settings EnvironmentSettings) GetClientCredentials() (ClientCredentialsConfig, error) {
|
||||
secret := settings.Values[ClientSecret]
|
||||
if secret == "" {
|
||||
logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetClientCredentials() missing client secret")
|
||||
return ClientCredentialsConfig{}, errors.New("missing client secret")
|
||||
}
|
||||
clientID, tenantID := settings.getClientAndTenant()
|
||||
@@ -161,6 +164,7 @@ func (settings EnvironmentSettings) GetClientCredentials() (ClientCredentialsCon
|
||||
func (settings EnvironmentSettings) GetClientCertificate() (ClientCertificateConfig, error) {
|
||||
certPath := settings.Values[CertificatePath]
|
||||
if certPath == "" {
|
||||
logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetClientCertificate() missing certificate path")
|
||||
return ClientCertificateConfig{}, errors.New("missing certificate path")
|
||||
}
|
||||
certPwd := settings.Values[CertificatePassword]
|
||||
@@ -177,6 +181,7 @@ func (settings EnvironmentSettings) GetUsernamePassword() (UsernamePasswordConfi
|
||||
username := settings.Values[Username]
|
||||
password := settings.Values[Password]
|
||||
if username == "" || password == "" {
|
||||
logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetUsernamePassword() missing username and/or password")
|
||||
return UsernamePasswordConfig{}, errors.New("missing username/password")
|
||||
}
|
||||
clientID, tenantID := settings.getClientAndTenant()
|
||||
@@ -211,35 +216,55 @@ func (settings EnvironmentSettings) GetDeviceFlow() DeviceFlowConfig {
|
||||
func (settings EnvironmentSettings) GetAuthorizer() (autorest.Authorizer, error) {
|
||||
//1.Client Credentials
|
||||
if c, e := settings.GetClientCredentials(); e == nil {
|
||||
logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using client secret credentials")
|
||||
return c.Authorizer()
|
||||
}
|
||||
|
||||
//2. Client Certificate
|
||||
if c, e := settings.GetClientCertificate(); e == nil {
|
||||
logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using client certificate credentials")
|
||||
return c.Authorizer()
|
||||
}
|
||||
|
||||
//3. Username Password
|
||||
if c, e := settings.GetUsernamePassword(); e == nil {
|
||||
logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using user name/password credentials")
|
||||
return c.Authorizer()
|
||||
}
|
||||
|
||||
// 4. MSI
|
||||
if !adal.MSIAvailable(context.Background(), nil) {
|
||||
return nil, errors.New("MSI not available")
|
||||
}
|
||||
logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using MSI authentication")
|
||||
return settings.GetMSI().Authorizer()
|
||||
}
|
||||
|
||||
// NewAuthorizerFromFile creates an Authorizer configured from a configuration file in the following order.
|
||||
// 1. Client credentials
|
||||
// 2. Client certificate
|
||||
func NewAuthorizerFromFile(baseURI string) (autorest.Authorizer, error) {
|
||||
// The path to the configuration file must be specified in the AZURE_AUTH_LOCATION environment variable.
|
||||
// resourceBaseURI - used to determine the resource type
|
||||
func NewAuthorizerFromFile(resourceBaseURI string) (autorest.Authorizer, error) {
|
||||
settings, err := GetSettingsFromFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if a, err := settings.ClientCredentialsAuthorizer(baseURI); err == nil {
|
||||
return settings.GetAuthorizer(resourceBaseURI)
|
||||
}
|
||||
|
||||
// GetAuthorizer create an Authorizer in the following order.
|
||||
// 1. Client credentials
|
||||
// 2. Client certificate
|
||||
// resourceBaseURI - used to determine the resource type
|
||||
func (settings FileSettings) GetAuthorizer(resourceBaseURI string) (autorest.Authorizer, error) {
|
||||
if resourceBaseURI == "" {
|
||||
resourceBaseURI = azure.PublicCloud.ServiceManagementEndpoint
|
||||
}
|
||||
if a, err := settings.ClientCredentialsAuthorizer(resourceBaseURI); err == nil {
|
||||
return a, err
|
||||
}
|
||||
if a, err := settings.ClientCertificateAuthorizer(baseURI); err == nil {
|
||||
if a, err := settings.ClientCertificateAuthorizer(resourceBaseURI); err == nil {
|
||||
return a, err
|
||||
}
|
||||
return nil, errors.New("auth file missing client and certificate credentials")
|
||||
@@ -248,6 +273,7 @@ func NewAuthorizerFromFile(baseURI string) (autorest.Authorizer, error) {
|
||||
// NewAuthorizerFromFileWithResource creates an Authorizer configured from a configuration file in the following order.
|
||||
// 1. Client credentials
|
||||
// 2. Client certificate
|
||||
// The path to the configuration file must be specified in the AZURE_AUTH_LOCATION environment variable.
|
||||
func NewAuthorizerFromFileWithResource(resource string) (autorest.Authorizer, error) {
|
||||
s, err := GetSettingsFromFile()
|
||||
if err != nil {
|
||||
@@ -466,7 +492,7 @@ func decode(b []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (settings FileSettings) getResourceForToken(baseURI string) (string, error) {
|
||||
// Compare dafault base URI from the SDK to the endpoints from the public cloud
|
||||
// Compare default base URI from the SDK to the endpoints from the public cloud
|
||||
// Base URI and token resource are the same string. This func finds the authentication
|
||||
// file field that matches the SDK base URI. The SDK defines the public cloud
|
||||
// endpoint as its default base URI
|
||||
@@ -544,7 +570,7 @@ func NewDeviceFlowConfig(clientID string, tenantID string) DeviceFlowConfig {
|
||||
}
|
||||
}
|
||||
|
||||
//AuthorizerConfig provides an authorizer from the configuration provided.
|
||||
// AuthorizerConfig provides an authorizer from the configuration provided.
|
||||
type AuthorizerConfig interface {
|
||||
Authorizer() (autorest.Authorizer, error)
|
||||
}
|
||||
@@ -599,6 +625,7 @@ type ClientCertificateConfig struct {
|
||||
CertificatePath string
|
||||
CertificatePassword string
|
||||
TenantID string
|
||||
AuxTenants []string
|
||||
AADEndpoint string
|
||||
Resource string
|
||||
}
|
||||
@@ -613,20 +640,44 @@ func (ccc ClientCertificateConfig) ServicePrincipalToken() (*adal.ServicePrincip
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read the certificate file (%s): %v", ccc.CertificatePath, err)
|
||||
}
|
||||
certificate, rsaPrivateKey, err := decodePkcs12(certData, ccc.CertificatePassword)
|
||||
certificate, rsaPrivateKey, err := adal.DecodePfxCertificateData(certData, ccc.CertificatePassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
|
||||
}
|
||||
return adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource)
|
||||
}
|
||||
|
||||
// MultiTenantServicePrincipalToken creates a MultiTenantServicePrincipalToken from client certificate.
|
||||
func (ccc ClientCertificateConfig) MultiTenantServicePrincipalToken() (*adal.MultiTenantServicePrincipalToken, error) {
|
||||
oauthConfig, err := adal.NewMultiTenantOAuthConfig(ccc.AADEndpoint, ccc.TenantID, ccc.AuxTenants, adal.OAuthOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certData, err := ioutil.ReadFile(ccc.CertificatePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read the certificate file (%s): %v", ccc.CertificatePath, err)
|
||||
}
|
||||
certificate, rsaPrivateKey, err := adal.DecodePfxCertificateData(certData, ccc.CertificatePassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
|
||||
}
|
||||
return adal.NewMultiTenantServicePrincipalTokenFromCertificate(oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource)
|
||||
}
|
||||
|
||||
// Authorizer gets an authorizer object from client certificate.
|
||||
func (ccc ClientCertificateConfig) Authorizer() (autorest.Authorizer, error) {
|
||||
spToken, err := ccc.ServicePrincipalToken()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get oauth token from certificate auth: %v", err)
|
||||
if len(ccc.AuxTenants) == 0 {
|
||||
spToken, err := ccc.ServicePrincipalToken()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get oauth token from certificate auth: %v", err)
|
||||
}
|
||||
return autorest.NewBearerAuthorizer(spToken), nil
|
||||
}
|
||||
return autorest.NewBearerAuthorizer(spToken), nil
|
||||
mtSPT, err := ccc.MultiTenantServicePrincipalToken()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get multitenant SPT from certificate auth: %v", err)
|
||||
}
|
||||
return autorest.NewMultiTenantServicePrincipalTokenAuthorizer(mtSPT), nil
|
||||
}
|
||||
|
||||
// DeviceFlowConfig provides the options to get a bearer authorizer using device flow authentication.
|
||||
@@ -665,20 +716,6 @@ func (dfc DeviceFlowConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken
|
||||
return adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, dfc.ClientID, dfc.Resource, *token)
|
||||
}
|
||||
|
||||
func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||
privateKey, certificate, err := pkcs12.Decode(pkcs, password)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey)
|
||||
if !isRsaKey {
|
||||
return nil, nil, fmt.Errorf("PKCS#12 certificate must contain an RSA private key")
|
||||
}
|
||||
|
||||
return certificate, rsaPrivateKey, nil
|
||||
}
|
||||
|
||||
// UsernamePasswordConfig provides the options to get a bearer authorizer from a username and a password.
|
||||
type UsernamePasswordConfig struct {
|
||||
ClientID string
|
||||
@@ -713,25 +750,23 @@ type MSIConfig struct {
|
||||
ClientID string
|
||||
}
|
||||
|
||||
// ServicePrincipalToken creates a ServicePrincipalToken from MSI.
|
||||
func (mc MSIConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
|
||||
spToken, err := adal.NewServicePrincipalTokenFromManagedIdentity(mc.Resource, &adal.ManagedIdentityOptions{
|
||||
ClientID: mc.ClientID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get oauth token from MSI: %v", err)
|
||||
}
|
||||
return spToken, nil
|
||||
}
|
||||
|
||||
// Authorizer gets the authorizer from MSI.
|
||||
func (mc MSIConfig) Authorizer() (autorest.Authorizer, error) {
|
||||
msiEndpoint, err := adal.GetMSIEndpoint()
|
||||
spToken, err := mc.ServicePrincipalToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var spToken *adal.ServicePrincipalToken
|
||||
if mc.ClientID == "" {
|
||||
spToken, err = adal.NewServicePrincipalTokenFromMSI(msiEndpoint, mc.Resource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get oauth token from MSI: %v", err)
|
||||
}
|
||||
} else {
|
||||
spToken, err = adal.NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, mc.Resource, mc.ClientID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get oauth token from MSI for user assigned identity: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return autorest.NewBearerAuthorizer(spToken), nil
|
||||
}
|
||||
|
||||
5
vendor/github.com/Azure/go-autorest/autorest/azure/auth/go_mod_tidy_hack.go
generated
vendored
5
vendor/github.com/Azure/go-autorest/autorest/azure/auth/go_mod_tidy_hack.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build modhack
|
||||
// +build modhack
|
||||
|
||||
package auth
|
||||
@@ -16,9 +17,9 @@ package auth
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This file, and the github.com/Azure/go-autorest/autorest import, won't actually become part of
|
||||
// This file, and the github.com/Azure/go-autorest import, won't actually become part of
|
||||
// the resultant binary.
|
||||
|
||||
// Necessary for safely adding multi-module repo.
|
||||
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
|
||||
import _ "github.com/Azure/go-autorest/autorest"
|
||||
import _ "github.com/Azure/go-autorest"
|
||||
|
||||
143
vendor/github.com/Azure/go-autorest/autorest/azure/azure.go
generated
vendored
143
vendor/github.com/Azure/go-autorest/autorest/azure/azure.go
generated
vendored
@@ -37,6 +37,9 @@ const (
|
||||
// should be included in the response.
|
||||
HeaderReturnClientID = "x-ms-return-client-request-id"
|
||||
|
||||
// HeaderContentType is the type of the content in the HTTP response.
|
||||
HeaderContentType = "Content-Type"
|
||||
|
||||
// HeaderRequestID is the Azure extension header of the service generated request ID returned
|
||||
// in the response.
|
||||
HeaderRequestID = "x-ms-request-id"
|
||||
@@ -65,7 +68,7 @@ func (se ServiceError) Error() string {
|
||||
if err != nil {
|
||||
result += fmt.Sprintf(" Details=%v", se.Details)
|
||||
}
|
||||
result += fmt.Sprintf(" Details=%v", string(d))
|
||||
result += fmt.Sprintf(" Details=%s", d)
|
||||
}
|
||||
|
||||
if se.InnerError != nil {
|
||||
@@ -73,7 +76,7 @@ func (se ServiceError) Error() string {
|
||||
if err != nil {
|
||||
result += fmt.Sprintf(" InnerError=%v", se.InnerError)
|
||||
}
|
||||
result += fmt.Sprintf(" InnerError=%v", string(d))
|
||||
result += fmt.Sprintf(" InnerError=%s", d)
|
||||
}
|
||||
|
||||
if se.AdditionalInfo != nil {
|
||||
@@ -81,7 +84,7 @@ func (se ServiceError) Error() string {
|
||||
if err != nil {
|
||||
result += fmt.Sprintf(" AdditionalInfo=%v", se.AdditionalInfo)
|
||||
}
|
||||
result += fmt.Sprintf(" AdditionalInfo=%v", string(d))
|
||||
result += fmt.Sprintf(" AdditionalInfo=%s", d)
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -89,54 +92,85 @@ func (se ServiceError) Error() string {
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface for the ServiceError type.
|
||||
func (se *ServiceError) UnmarshalJSON(b []byte) error {
|
||||
// per the OData v4 spec the details field must be an array of JSON objects.
|
||||
// unfortunately not all services adhear to the spec and just return a single
|
||||
// object instead of an array with one object. so we have to perform some
|
||||
// shenanigans to accommodate both cases.
|
||||
// http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793091
|
||||
|
||||
type serviceError1 struct {
|
||||
type serviceErrorInternal struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Target *string `json:"target"`
|
||||
Details []map[string]interface{} `json:"details"`
|
||||
InnerError map[string]interface{} `json:"innererror"`
|
||||
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
|
||||
Target *string `json:"target,omitempty"`
|
||||
AdditionalInfo []map[string]interface{} `json:"additionalInfo,omitempty"`
|
||||
// not all services conform to the OData v4 spec.
|
||||
// the following fields are where we've seen discrepancies
|
||||
|
||||
// spec calls for []map[string]interface{} but have seen map[string]interface{}
|
||||
Details interface{} `json:"details,omitempty"`
|
||||
|
||||
// spec calls for map[string]interface{} but have seen []map[string]interface{} and string
|
||||
InnerError interface{} `json:"innererror,omitempty"`
|
||||
}
|
||||
|
||||
type serviceError2 struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Target *string `json:"target"`
|
||||
Details map[string]interface{} `json:"details"`
|
||||
InnerError map[string]interface{} `json:"innererror"`
|
||||
AdditionalInfo []map[string]interface{} `json:"additionalInfo"`
|
||||
sei := serviceErrorInternal{}
|
||||
if err := json.Unmarshal(b, &sei); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
se1 := serviceError1{}
|
||||
err := json.Unmarshal(b, &se1)
|
||||
if err == nil {
|
||||
se.populate(se1.Code, se1.Message, se1.Target, se1.Details, se1.InnerError, se1.AdditionalInfo)
|
||||
return nil
|
||||
// copy the fields we know to be correct
|
||||
se.AdditionalInfo = sei.AdditionalInfo
|
||||
se.Code = sei.Code
|
||||
se.Message = sei.Message
|
||||
se.Target = sei.Target
|
||||
|
||||
// converts an []interface{} to []map[string]interface{}
|
||||
arrayOfObjs := func(v interface{}) ([]map[string]interface{}, bool) {
|
||||
arrayOf, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
final := []map[string]interface{}{}
|
||||
for _, item := range arrayOf {
|
||||
as, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
final = append(final, as)
|
||||
}
|
||||
return final, true
|
||||
}
|
||||
|
||||
se2 := serviceError2{}
|
||||
err = json.Unmarshal(b, &se2)
|
||||
if err == nil {
|
||||
se.populate(se2.Code, se2.Message, se2.Target, nil, se2.InnerError, se2.AdditionalInfo)
|
||||
se.Details = append(se.Details, se2.Details)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// convert the remaining fields, falling back to raw JSON if necessary
|
||||
|
||||
func (se *ServiceError) populate(code, message string, target *string, details []map[string]interface{}, inner map[string]interface{}, additional []map[string]interface{}) {
|
||||
se.Code = code
|
||||
se.Message = message
|
||||
se.Target = target
|
||||
se.Details = details
|
||||
se.InnerError = inner
|
||||
se.AdditionalInfo = additional
|
||||
if c, ok := arrayOfObjs(sei.Details); ok {
|
||||
se.Details = c
|
||||
} else if c, ok := sei.Details.(map[string]interface{}); ok {
|
||||
se.Details = []map[string]interface{}{c}
|
||||
} else if sei.Details != nil {
|
||||
// stuff into Details
|
||||
se.Details = []map[string]interface{}{
|
||||
{"raw": sei.Details},
|
||||
}
|
||||
}
|
||||
|
||||
if c, ok := sei.InnerError.(map[string]interface{}); ok {
|
||||
se.InnerError = c
|
||||
} else if c, ok := arrayOfObjs(sei.InnerError); ok {
|
||||
// if there's only one error extract it
|
||||
if len(c) == 1 {
|
||||
se.InnerError = c[0]
|
||||
} else {
|
||||
// multiple errors, stuff them into the value
|
||||
se.InnerError = map[string]interface{}{
|
||||
"multi": c,
|
||||
}
|
||||
}
|
||||
} else if c, ok := sei.InnerError.(string); ok {
|
||||
se.InnerError = map[string]interface{}{"error": c}
|
||||
} else if sei.InnerError != nil {
|
||||
// stuff into InnerError
|
||||
se.InnerError = map[string]interface{}{
|
||||
"raw": sei.InnerError,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestError describes an error response returned by Azure service.
|
||||
@@ -171,8 +205,13 @@ type Resource struct {
|
||||
ResourceName string
|
||||
}
|
||||
|
||||
// String function returns a string in form of azureResourceID
|
||||
func (r Resource) String() string {
|
||||
return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/%s/%s/%s", r.SubscriptionID, r.ResourceGroup, r.Provider, r.ResourceType, r.ResourceName)
|
||||
}
|
||||
|
||||
// ParseResourceID parses a resource ID into a ResourceDetails struct.
|
||||
// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-resource#return-value-4.
|
||||
// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions-resource?tabs=json#resourceid.
|
||||
func ParseResourceID(resourceID string) (Resource, error) {
|
||||
|
||||
const resourceIDPatternText = `(?i)subscriptions/(.+)/resourceGroups/(.+)/providers/(.+?)/(.+?)/(.+)`
|
||||
@@ -296,22 +335,36 @@ func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator {
|
||||
b, decodeErr := autorest.CopyAndDecode(encodedAs, resp.Body, &e)
|
||||
resp.Body = ioutil.NopCloser(&b)
|
||||
if decodeErr != nil {
|
||||
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr)
|
||||
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, decodeErr)
|
||||
}
|
||||
if e.ServiceError == nil {
|
||||
// Check if error is unwrapped ServiceError
|
||||
decoder := autorest.NewDecoder(encodedAs, bytes.NewReader(b.Bytes()))
|
||||
if err := decoder.Decode(&e.ServiceError); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, err)
|
||||
}
|
||||
|
||||
// for example, should the API return the literal value `null` as the response
|
||||
if e.ServiceError == nil {
|
||||
e.ServiceError = &ServiceError{
|
||||
Code: "Unknown",
|
||||
Message: "Unknown service error",
|
||||
Details: []map[string]interface{}{
|
||||
{
|
||||
"HttpResponse.Body": b.String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
if e.ServiceError.Message == "" {
|
||||
|
||||
if e.ServiceError != nil && e.ServiceError.Message == "" {
|
||||
// if we're here it means the returned error wasn't OData v4 compliant.
|
||||
// try to unmarshal the body in hopes of getting something.
|
||||
rawBody := map[string]interface{}{}
|
||||
decoder := autorest.NewDecoder(encodedAs, bytes.NewReader(b.Bytes()))
|
||||
if err := decoder.Decode(&rawBody); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b, err)
|
||||
}
|
||||
|
||||
e.ServiceError = &ServiceError{
|
||||
|
||||
5
vendor/github.com/Azure/go-autorest/autorest/azure/cli/go_mod_tidy_hack.go
generated
vendored
5
vendor/github.com/Azure/go-autorest/autorest/azure/cli/go_mod_tidy_hack.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build modhack
|
||||
// +build modhack
|
||||
|
||||
package cli
|
||||
@@ -16,9 +17,9 @@ package cli
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This file, and the github.com/Azure/go-autorest/autorest import, won't actually become part of
|
||||
// This file, and the github.com/Azure/go-autorest import, won't actually become part of
|
||||
// the resultant binary.
|
||||
|
||||
// Necessary for safely adding multi-module repo.
|
||||
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
|
||||
import _ "github.com/Azure/go-autorest/autorest"
|
||||
import _ "github.com/Azure/go-autorest"
|
||||
|
||||
104
vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go
generated
vendored
104
vendor/github.com/Azure/go-autorest/autorest/azure/cli/token.go
generated
vendored
@@ -124,6 +124,83 @@ func LoadTokens(path string) ([]Token, error) {
|
||||
|
||||
// GetTokenFromCLI gets a token using Azure CLI 2.0 for local development scenarios.
|
||||
func GetTokenFromCLI(resource string) (*Token, error) {
|
||||
return GetTokenFromCLIWithParams(GetAccessTokenParams{Resource: resource})
|
||||
}
|
||||
|
||||
// GetAccessTokenParams is the parameter struct of GetTokenFromCLIWithParams
|
||||
type GetAccessTokenParams struct {
|
||||
Resource string
|
||||
ResourceType string
|
||||
Subscription string
|
||||
Tenant string
|
||||
}
|
||||
|
||||
// GetTokenFromCLIWithParams gets a token using Azure CLI 2.0 for local development scenarios.
|
||||
func GetTokenFromCLIWithParams(params GetAccessTokenParams) (*Token, error) {
|
||||
cliCmd := GetAzureCLICommand()
|
||||
|
||||
cliCmd.Args = append(cliCmd.Args, "account", "get-access-token", "-o", "json")
|
||||
if params.Resource != "" {
|
||||
if err := validateParameter(params.Resource); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cliCmd.Args = append(cliCmd.Args, "--resource", params.Resource)
|
||||
}
|
||||
if params.ResourceType != "" {
|
||||
if err := validateParameter(params.ResourceType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cliCmd.Args = append(cliCmd.Args, "--resource-type", params.ResourceType)
|
||||
}
|
||||
if params.Subscription != "" {
|
||||
if err := validateParameter(params.Subscription); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cliCmd.Args = append(cliCmd.Args, "--subscription", params.Subscription)
|
||||
}
|
||||
if params.Tenant != "" {
|
||||
if err := validateParameter(params.Tenant); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cliCmd.Args = append(cliCmd.Args, "--tenant", params.Tenant)
|
||||
}
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cliCmd.Stderr = &stderr
|
||||
|
||||
output, err := cliCmd.Output()
|
||||
if err != nil {
|
||||
if stderr.Len() > 0 {
|
||||
return nil, fmt.Errorf("Invoking Azure CLI failed with the following error: %s", stderr.String())
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invoking Azure CLI failed with the following error: %s", err.Error())
|
||||
}
|
||||
|
||||
tokenResponse := Token{}
|
||||
err = json.Unmarshal(output, &tokenResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tokenResponse, err
|
||||
}
|
||||
|
||||
func validateParameter(param string) error {
|
||||
// Validate parameters, since it gets sent as a command line argument to Azure CLI
|
||||
const invalidResourceErrorTemplate = "Parameter %s is not in expected format. Only alphanumeric characters, [dot], [colon], [hyphen], and [forward slash] are allowed."
|
||||
match, err := regexp.MatchString("^[0-9a-zA-Z-.:/]+$", param)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !match {
|
||||
return fmt.Errorf(invalidResourceErrorTemplate, param)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAzureCLICommand can be used to run arbitrary Azure CLI command
|
||||
func GetAzureCLICommand() *exec.Cmd {
|
||||
// This is the path that a developer can set to tell this class what the install path for Azure CLI is.
|
||||
const azureCLIPath = "AzureCLIPath"
|
||||
|
||||
@@ -133,16 +210,6 @@ func GetTokenFromCLI(resource string) (*Token, error) {
|
||||
// Default path for non-Windows.
|
||||
const azureCLIDefaultPath = "/bin:/sbin:/usr/bin:/usr/local/bin"
|
||||
|
||||
// Validate resource, since it gets sent as a command line argument to Azure CLI
|
||||
const invalidResourceErrorTemplate = "Resource %s is not in expected format. Only alphanumeric characters, [dot], [colon], [hyphen], and [forward slash] are allowed."
|
||||
match, err := regexp.MatchString("^[0-9a-zA-Z-.:/]+$", resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !match {
|
||||
return nil, fmt.Errorf(invalidResourceErrorTemplate, resource)
|
||||
}
|
||||
|
||||
// Execute Azure CLI to get token
|
||||
var cliCmd *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
@@ -155,21 +222,6 @@ func GetTokenFromCLI(resource string) (*Token, error) {
|
||||
cliCmd.Env = os.Environ()
|
||||
cliCmd.Env = append(cliCmd.Env, fmt.Sprintf("PATH=%s:%s", os.Getenv(azureCLIPath), azureCLIDefaultPath))
|
||||
}
|
||||
cliCmd.Args = append(cliCmd.Args, "account", "get-access-token", "-o", "json", "--resource", resource)
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cliCmd.Stderr = &stderr
|
||||
|
||||
output, err := cliCmd.Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invoking Azure CLI failed with the following error: %s", stderr.String())
|
||||
}
|
||||
|
||||
tokenResponse := Token{}
|
||||
err = json.Unmarshal(output, &tokenResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tokenResponse, err
|
||||
return cliCmd
|
||||
}
|
||||
|
||||
103
vendor/github.com/Azure/go-autorest/autorest/azure/environments.go
generated
vendored
103
vendor/github.com/Azure/go-autorest/autorest/azure/environments.go
generated
vendored
@@ -34,8 +34,10 @@ const (
|
||||
var environments = map[string]Environment{
|
||||
"AZURECHINACLOUD": ChinaCloud,
|
||||
"AZUREGERMANCLOUD": GermanCloud,
|
||||
"AZURECLOUD": PublicCloud,
|
||||
"AZUREPUBLICCLOUD": PublicCloud,
|
||||
"AZUREUSGOVERNMENTCLOUD": USGovernmentCloud,
|
||||
"AZUREUSGOVERNMENT": USGovernmentCloud,
|
||||
"AZUREUSGOVERNMENTCLOUD": USGovernmentCloud, //TODO: deprecate
|
||||
}
|
||||
|
||||
// ResourceIdentifier contains a set of Azure resource IDs.
|
||||
@@ -45,7 +47,14 @@ type ResourceIdentifier struct {
|
||||
Datalake string `json:"datalake"`
|
||||
Batch string `json:"batch"`
|
||||
OperationalInsights string `json:"operationalInsights"`
|
||||
OSSRDBMS string `json:"ossRDBMS"`
|
||||
Storage string `json:"storage"`
|
||||
Synapse string `json:"synapse"`
|
||||
ServiceBus string `json:"serviceBus"`
|
||||
SQLDatabase string `json:"sqlDatabase"`
|
||||
CosmosDB string `json:"cosmosDB"`
|
||||
ManagedHSM string `json:"managedHSM"`
|
||||
MicrosoftGraph string `json:"microsoftGraph"`
|
||||
}
|
||||
|
||||
// Environment represents a set of endpoints for each of Azure's Clouds.
|
||||
@@ -58,19 +67,28 @@ type Environment struct {
|
||||
ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"`
|
||||
GalleryEndpoint string `json:"galleryEndpoint"`
|
||||
KeyVaultEndpoint string `json:"keyVaultEndpoint"`
|
||||
ManagedHSMEndpoint string `json:"managedHSMEndpoint"`
|
||||
GraphEndpoint string `json:"graphEndpoint"`
|
||||
ServiceBusEndpoint string `json:"serviceBusEndpoint"`
|
||||
BatchManagementEndpoint string `json:"batchManagementEndpoint"`
|
||||
MicrosoftGraphEndpoint string `json:"microsoftGraphEndpoint"`
|
||||
StorageEndpointSuffix string `json:"storageEndpointSuffix"`
|
||||
CosmosDBDNSSuffix string `json:"cosmosDBDNSSuffix"`
|
||||
MariaDBDNSSuffix string `json:"mariaDBDNSSuffix"`
|
||||
MySQLDatabaseDNSSuffix string `json:"mySqlDatabaseDNSSuffix"`
|
||||
PostgresqlDatabaseDNSSuffix string `json:"postgresqlDatabaseDNSSuffix"`
|
||||
SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"`
|
||||
TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"`
|
||||
KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"`
|
||||
ManagedHSMDNSSuffix string `json:"managedHSMDNSSuffix"`
|
||||
ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"`
|
||||
ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"`
|
||||
ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"`
|
||||
ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"`
|
||||
CosmosDBDNSSuffix string `json:"cosmosDBDNSSuffix"`
|
||||
TokenAudience string `json:"tokenAudience"`
|
||||
APIManagementHostNameSuffix string `json:"apiManagementHostNameSuffix"`
|
||||
SynapseEndpointSuffix string `json:"synapseEndpointSuffix"`
|
||||
DatalakeSuffix string `json:"datalakeSuffix"`
|
||||
ResourceIdentifiers ResourceIdentifier `json:"resourceIdentifiers"`
|
||||
}
|
||||
|
||||
@@ -85,26 +103,42 @@ var (
|
||||
ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
|
||||
GalleryEndpoint: "https://gallery.azure.com/",
|
||||
KeyVaultEndpoint: "https://vault.azure.net/",
|
||||
ManagedHSMEndpoint: "https://managedhsm.azure.net/",
|
||||
GraphEndpoint: "https://graph.windows.net/",
|
||||
ServiceBusEndpoint: "https://servicebus.windows.net/",
|
||||
BatchManagementEndpoint: "https://batch.core.windows.net/",
|
||||
MicrosoftGraphEndpoint: "https://graph.microsoft.com/",
|
||||
StorageEndpointSuffix: "core.windows.net",
|
||||
CosmosDBDNSSuffix: "documents.azure.com",
|
||||
MariaDBDNSSuffix: "mariadb.database.azure.com",
|
||||
MySQLDatabaseDNSSuffix: "mysql.database.azure.com",
|
||||
PostgresqlDatabaseDNSSuffix: "postgres.database.azure.com",
|
||||
SQLDatabaseDNSSuffix: "database.windows.net",
|
||||
TrafficManagerDNSSuffix: "trafficmanager.net",
|
||||
KeyVaultDNSSuffix: "vault.azure.net",
|
||||
ManagedHSMDNSSuffix: "managedhsm.azure.net",
|
||||
ServiceBusEndpointSuffix: "servicebus.windows.net",
|
||||
ServiceManagementVMDNSSuffix: "cloudapp.net",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.azure.com",
|
||||
ContainerRegistryDNSSuffix: "azurecr.io",
|
||||
CosmosDBDNSSuffix: "documents.azure.com",
|
||||
TokenAudience: "https://management.azure.com/",
|
||||
APIManagementHostNameSuffix: "azure-api.net",
|
||||
SynapseEndpointSuffix: "dev.azuresynapse.net",
|
||||
DatalakeSuffix: "azuredatalakestore.net",
|
||||
ResourceIdentifiers: ResourceIdentifier{
|
||||
Graph: "https://graph.windows.net/",
|
||||
KeyVault: "https://vault.azure.net",
|
||||
Datalake: "https://datalake.azure.net/",
|
||||
Batch: "https://batch.core.windows.net/",
|
||||
OperationalInsights: "https://api.loganalytics.io",
|
||||
OSSRDBMS: "https://ossrdbms-aad.database.windows.net",
|
||||
Storage: "https://storage.azure.com/",
|
||||
Synapse: "https://dev.azuresynapse.net",
|
||||
ServiceBus: "https://servicebus.azure.net/",
|
||||
SQLDatabase: "https://database.windows.net/",
|
||||
CosmosDB: "https://cosmos.azure.com",
|
||||
ManagedHSM: "https://managedhsm.azure.net",
|
||||
MicrosoftGraph: "https://graph.microsoft.com/",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -118,26 +152,42 @@ var (
|
||||
ActiveDirectoryEndpoint: "https://login.microsoftonline.us/",
|
||||
GalleryEndpoint: "https://gallery.usgovcloudapi.net/",
|
||||
KeyVaultEndpoint: "https://vault.usgovcloudapi.net/",
|
||||
ManagedHSMEndpoint: NotAvailable,
|
||||
GraphEndpoint: "https://graph.windows.net/",
|
||||
ServiceBusEndpoint: "https://servicebus.usgovcloudapi.net/",
|
||||
BatchManagementEndpoint: "https://batch.core.usgovcloudapi.net/",
|
||||
MicrosoftGraphEndpoint: "https://graph.microsoft.us/",
|
||||
StorageEndpointSuffix: "core.usgovcloudapi.net",
|
||||
CosmosDBDNSSuffix: "documents.azure.us",
|
||||
MariaDBDNSSuffix: "mariadb.database.usgovcloudapi.net",
|
||||
MySQLDatabaseDNSSuffix: "mysql.database.usgovcloudapi.net",
|
||||
PostgresqlDatabaseDNSSuffix: "postgres.database.usgovcloudapi.net",
|
||||
SQLDatabaseDNSSuffix: "database.usgovcloudapi.net",
|
||||
TrafficManagerDNSSuffix: "usgovtrafficmanager.net",
|
||||
KeyVaultDNSSuffix: "vault.usgovcloudapi.net",
|
||||
ManagedHSMDNSSuffix: NotAvailable,
|
||||
ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net",
|
||||
ServiceManagementVMDNSSuffix: "usgovcloudapp.net",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.usgovcloudapi.net",
|
||||
ContainerRegistryDNSSuffix: "azurecr.us",
|
||||
CosmosDBDNSSuffix: "documents.azure.us",
|
||||
TokenAudience: "https://management.usgovcloudapi.net/",
|
||||
APIManagementHostNameSuffix: "azure-api.us",
|
||||
SynapseEndpointSuffix: "dev.azuresynapse.usgovcloudapi.net",
|
||||
DatalakeSuffix: NotAvailable,
|
||||
ResourceIdentifiers: ResourceIdentifier{
|
||||
Graph: "https://graph.windows.net/",
|
||||
KeyVault: "https://vault.usgovcloudapi.net",
|
||||
Datalake: NotAvailable,
|
||||
Batch: "https://batch.core.usgovcloudapi.net/",
|
||||
OperationalInsights: "https://api.loganalytics.us",
|
||||
OSSRDBMS: "https://ossrdbms-aad.database.usgovcloudapi.net",
|
||||
Storage: "https://storage.azure.com/",
|
||||
Synapse: "https://dev.azuresynapse.usgovcloudapi.net",
|
||||
ServiceBus: "https://servicebus.azure.net/",
|
||||
SQLDatabase: "https://database.usgovcloudapi.net/",
|
||||
CosmosDB: "https://cosmos.azure.com",
|
||||
ManagedHSM: NotAvailable,
|
||||
MicrosoftGraph: "https://graph.microsoft.us/",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -151,26 +201,42 @@ var (
|
||||
ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/",
|
||||
GalleryEndpoint: "https://gallery.chinacloudapi.cn/",
|
||||
KeyVaultEndpoint: "https://vault.azure.cn/",
|
||||
ManagedHSMEndpoint: NotAvailable,
|
||||
GraphEndpoint: "https://graph.chinacloudapi.cn/",
|
||||
ServiceBusEndpoint: "https://servicebus.chinacloudapi.cn/",
|
||||
BatchManagementEndpoint: "https://batch.chinacloudapi.cn/",
|
||||
MicrosoftGraphEndpoint: "https://microsoftgraph.chinacloudapi.cn/",
|
||||
StorageEndpointSuffix: "core.chinacloudapi.cn",
|
||||
CosmosDBDNSSuffix: "documents.azure.cn",
|
||||
MariaDBDNSSuffix: "mariadb.database.chinacloudapi.cn",
|
||||
MySQLDatabaseDNSSuffix: "mysql.database.chinacloudapi.cn",
|
||||
PostgresqlDatabaseDNSSuffix: "postgres.database.chinacloudapi.cn",
|
||||
SQLDatabaseDNSSuffix: "database.chinacloudapi.cn",
|
||||
TrafficManagerDNSSuffix: "trafficmanager.cn",
|
||||
KeyVaultDNSSuffix: "vault.azure.cn",
|
||||
ManagedHSMDNSSuffix: NotAvailable,
|
||||
ServiceBusEndpointSuffix: "servicebus.chinacloudapi.cn",
|
||||
ServiceManagementVMDNSSuffix: "chinacloudapp.cn",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.azure.cn",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.chinacloudapi.cn",
|
||||
ContainerRegistryDNSSuffix: "azurecr.cn",
|
||||
CosmosDBDNSSuffix: "documents.azure.cn",
|
||||
TokenAudience: "https://management.chinacloudapi.cn/",
|
||||
APIManagementHostNameSuffix: "azure-api.cn",
|
||||
SynapseEndpointSuffix: "dev.azuresynapse.azure.cn",
|
||||
DatalakeSuffix: NotAvailable,
|
||||
ResourceIdentifiers: ResourceIdentifier{
|
||||
Graph: "https://graph.chinacloudapi.cn/",
|
||||
KeyVault: "https://vault.azure.cn",
|
||||
Datalake: NotAvailable,
|
||||
Batch: "https://batch.chinacloudapi.cn/",
|
||||
OperationalInsights: NotAvailable,
|
||||
OSSRDBMS: "https://ossrdbms-aad.database.chinacloudapi.cn",
|
||||
Storage: "https://storage.azure.com/",
|
||||
Synapse: "https://dev.azuresynapse.net",
|
||||
ServiceBus: "https://servicebus.azure.net/",
|
||||
SQLDatabase: "https://database.chinacloudapi.cn/",
|
||||
CosmosDB: "https://cosmos.azure.com",
|
||||
ManagedHSM: NotAvailable,
|
||||
MicrosoftGraph: "https://microsoftgraph.chinacloudapi.cn",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -184,26 +250,42 @@ var (
|
||||
ActiveDirectoryEndpoint: "https://login.microsoftonline.de/",
|
||||
GalleryEndpoint: "https://gallery.cloudapi.de/",
|
||||
KeyVaultEndpoint: "https://vault.microsoftazure.de/",
|
||||
ManagedHSMEndpoint: NotAvailable,
|
||||
GraphEndpoint: "https://graph.cloudapi.de/",
|
||||
ServiceBusEndpoint: "https://servicebus.cloudapi.de/",
|
||||
BatchManagementEndpoint: "https://batch.cloudapi.de/",
|
||||
MicrosoftGraphEndpoint: NotAvailable,
|
||||
StorageEndpointSuffix: "core.cloudapi.de",
|
||||
CosmosDBDNSSuffix: "documents.microsoftazure.de",
|
||||
MariaDBDNSSuffix: "mariadb.database.cloudapi.de",
|
||||
MySQLDatabaseDNSSuffix: "mysql.database.cloudapi.de",
|
||||
PostgresqlDatabaseDNSSuffix: "postgres.database.cloudapi.de",
|
||||
SQLDatabaseDNSSuffix: "database.cloudapi.de",
|
||||
TrafficManagerDNSSuffix: "azuretrafficmanager.de",
|
||||
KeyVaultDNSSuffix: "vault.microsoftazure.de",
|
||||
ManagedHSMDNSSuffix: NotAvailable,
|
||||
ServiceBusEndpointSuffix: "servicebus.cloudapi.de",
|
||||
ServiceManagementVMDNSSuffix: "azurecloudapp.de",
|
||||
ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de",
|
||||
ContainerRegistryDNSSuffix: NotAvailable,
|
||||
CosmosDBDNSSuffix: "documents.microsoftazure.de",
|
||||
TokenAudience: "https://management.microsoftazure.de/",
|
||||
APIManagementHostNameSuffix: NotAvailable,
|
||||
SynapseEndpointSuffix: NotAvailable,
|
||||
DatalakeSuffix: NotAvailable,
|
||||
ResourceIdentifiers: ResourceIdentifier{
|
||||
Graph: "https://graph.cloudapi.de/",
|
||||
KeyVault: "https://vault.microsoftazure.de",
|
||||
Datalake: NotAvailable,
|
||||
Batch: "https://batch.cloudapi.de/",
|
||||
OperationalInsights: NotAvailable,
|
||||
OSSRDBMS: "https://ossrdbms-aad.database.cloudapi.de",
|
||||
Storage: "https://storage.azure.com/",
|
||||
Synapse: NotAvailable,
|
||||
ServiceBus: "https://servicebus.azure.net/",
|
||||
SQLDatabase: "https://database.cloudapi.de/",
|
||||
CosmosDB: "https://cosmos.azure.com",
|
||||
ManagedHSM: NotAvailable,
|
||||
MicrosoftGraph: NotAvailable,
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -242,3 +324,8 @@ func EnvironmentFromFile(location string) (unmarshaled Environment, err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SetEnvironment updates the environment map with the specified values.
|
||||
func SetEnvironment(name string, env Environment) {
|
||||
environments[strings.ToUpper(name)] = env
|
||||
}
|
||||
|
||||
2
vendor/github.com/Azure/go-autorest/autorest/azure/rp.go
generated
vendored
2
vendor/github.com/Azure/go-autorest/autorest/azure/rp.go
generated
vendored
@@ -64,7 +64,7 @@ func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator {
|
||||
if re.ServiceError != nil && re.ServiceError.Code == "MissingSubscriptionRegistration" {
|
||||
regErr := register(client, r, re)
|
||||
if regErr != nil {
|
||||
return resp, fmt.Errorf("failed auto registering Resource Provider: %s. Original error: %s", regErr, err)
|
||||
return resp, fmt.Errorf("failed auto registering Resource Provider: %s. Original error: %w", regErr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
vendor/github.com/Azure/go-autorest/autorest/client.go
generated
vendored
9
vendor/github.com/Azure/go-autorest/autorest/client.go
generated
vendored
@@ -17,6 +17,7 @@ package autorest
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -30,7 +31,7 @@ import (
|
||||
|
||||
const (
|
||||
// DefaultPollingDelay is a reasonable delay between polling requests.
|
||||
DefaultPollingDelay = 60 * time.Second
|
||||
DefaultPollingDelay = 30 * time.Second
|
||||
|
||||
// DefaultPollingDuration is a reasonable total polling duration.
|
||||
DefaultPollingDuration = 15 * time.Minute
|
||||
@@ -165,7 +166,8 @@ type Client struct {
|
||||
// Setting this to zero will use the provided context to control the duration.
|
||||
PollingDuration time.Duration
|
||||
|
||||
// RetryAttempts sets the default number of retry attempts for client.
|
||||
// RetryAttempts sets the total number of times the client will attempt to make an HTTP request.
|
||||
// Set the value to 1 to disable retries. DO NOT set the value to less than 1.
|
||||
RetryAttempts int
|
||||
|
||||
// RetryDuration sets the delay duration for retries.
|
||||
@@ -259,6 +261,9 @@ func (c Client) Do(r *http.Request) (*http.Response, error) {
|
||||
},
|
||||
})
|
||||
resp, err := SendWithSender(c.sender(tls.RenegotiateNever), r)
|
||||
if resp == nil && err == nil {
|
||||
err = errors.New("autorest: received nil response and error")
|
||||
}
|
||||
logger.Instance.WriteResponse(resp, logger.Filter{})
|
||||
Respond(resp, c.ByInspecting())
|
||||
return resp, err
|
||||
|
||||
4
vendor/github.com/Azure/go-autorest/autorest/date/go_mod_tidy_hack.go
generated
vendored
4
vendor/github.com/Azure/go-autorest/autorest/date/go_mod_tidy_hack.go
generated
vendored
@@ -16,9 +16,9 @@ package date
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This file, and the github.com/Azure/go-autorest/autorest import, won't actually become part of
|
||||
// This file, and the github.com/Azure/go-autorest import, won't actually become part of
|
||||
// the resultant binary.
|
||||
|
||||
// Necessary for safely adding multi-module repo.
|
||||
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
|
||||
import _ "github.com/Azure/go-autorest/autorest"
|
||||
import _ "github.com/Azure/go-autorest"
|
||||
|
||||
5
vendor/github.com/Azure/go-autorest/autorest/error.go
generated
vendored
5
vendor/github.com/Azure/go-autorest/autorest/error.go
generated
vendored
@@ -96,3 +96,8 @@ func (e DetailedError) Error() string {
|
||||
}
|
||||
return fmt.Sprintf("%s#%s: %s: StatusCode=%d -- Original Error: %v", e.PackageType, e.Method, e.Message, e.StatusCode, e.Original)
|
||||
}
|
||||
|
||||
// Unwrap returns the original error.
|
||||
func (e DetailedError) Unwrap() error {
|
||||
return e.Original
|
||||
}
|
||||
|
||||
25
vendor/github.com/Azure/go-autorest/autorest/go_mod_tidy_hack.go
generated
vendored
Normal file
25
vendor/github.com/Azure/go-autorest/autorest/go_mod_tidy_hack.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build modhack
|
||||
// +build modhack
|
||||
|
||||
package autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// 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, and the github.com/Azure/go-autorest import, won't actually become part of
|
||||
// the resultant binary.
|
||||
|
||||
// Necessary for safely adding multi-module repo.
|
||||
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
|
||||
import _ "github.com/Azure/go-autorest"
|
||||
31
vendor/github.com/Azure/go-autorest/autorest/preparer.go
generated
vendored
31
vendor/github.com/Azure/go-autorest/autorest/preparer.go
generated
vendored
@@ -127,10 +127,7 @@ func WithHeader(header string, value string) PrepareDecorator {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
r, err := p.Prepare(r)
|
||||
if err == nil {
|
||||
if r.Header == nil {
|
||||
r.Header = make(http.Header)
|
||||
}
|
||||
r.Header.Set(http.CanonicalHeaderKey(header), value)
|
||||
setHeader(r, http.CanonicalHeaderKey(header), value)
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
@@ -230,7 +227,7 @@ func AsPost() PrepareDecorator { return WithMethod("POST") }
|
||||
func AsPut() PrepareDecorator { return WithMethod("PUT") }
|
||||
|
||||
// WithBaseURL returns a PrepareDecorator that populates the http.Request with a url.URL constructed
|
||||
// from the supplied baseUrl.
|
||||
// from the supplied baseUrl. Query parameters will be encoded as required.
|
||||
func WithBaseURL(baseURL string) PrepareDecorator {
|
||||
return func(p Preparer) Preparer {
|
||||
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
|
||||
@@ -241,11 +238,18 @@ func WithBaseURL(baseURL string) PrepareDecorator {
|
||||
return r, err
|
||||
}
|
||||
if u.Scheme == "" {
|
||||
err = fmt.Errorf("autorest: No scheme detected in URL %s", baseURL)
|
||||
return r, fmt.Errorf("autorest: No scheme detected in URL %s", baseURL)
|
||||
}
|
||||
if err == nil {
|
||||
r.URL = u
|
||||
if u.RawQuery != "" {
|
||||
// handle unencoded semicolons (ideally the server would send them already encoded)
|
||||
u.RawQuery = strings.Replace(u.RawQuery, ";", "%3B", -1)
|
||||
q, err := url.ParseQuery(u.RawQuery)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
}
|
||||
r.URL = u
|
||||
}
|
||||
return r, err
|
||||
})
|
||||
@@ -290,10 +294,7 @@ func WithFormData(v url.Values) PrepareDecorator {
|
||||
if err == nil {
|
||||
s := v.Encode()
|
||||
|
||||
if r.Header == nil {
|
||||
r.Header = make(http.Header)
|
||||
}
|
||||
r.Header.Set(http.CanonicalHeaderKey(headerContentType), mimeTypeFormPost)
|
||||
setHeader(r, http.CanonicalHeaderKey(headerContentType), mimeTypeFormPost)
|
||||
r.ContentLength = int64(len(s))
|
||||
r.Body = ioutil.NopCloser(strings.NewReader(s))
|
||||
}
|
||||
@@ -329,10 +330,7 @@ func WithMultiPartFormData(formDataParameters map[string]interface{}) PrepareDec
|
||||
if err = writer.Close(); err != nil {
|
||||
return r, err
|
||||
}
|
||||
if r.Header == nil {
|
||||
r.Header = make(http.Header)
|
||||
}
|
||||
r.Header.Set(http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType())
|
||||
setHeader(r, http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType())
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes()))
|
||||
r.ContentLength = int64(body.Len())
|
||||
return r, err
|
||||
@@ -437,6 +435,7 @@ func WithXML(v interface{}) PrepareDecorator {
|
||||
bytesWithHeader := []byte(withHeader)
|
||||
|
||||
r.ContentLength = int64(len(bytesWithHeader))
|
||||
setHeader(r, headerContentLength, fmt.Sprintf("%d", len(bytesWithHeader)))
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(bytesWithHeader))
|
||||
}
|
||||
}
|
||||
|
||||
1
vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go
generated
vendored
1
vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build !go1.8
|
||||
// +build !go1.8
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
|
||||
1
vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go
generated
vendored
1
vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build go1.8
|
||||
// +build go1.8
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
|
||||
93
vendor/github.com/Azure/go-autorest/autorest/sender.go
generated
vendored
93
vendor/github.com/Azure/go-autorest/autorest/sender.go
generated
vendored
@@ -20,14 +20,34 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/logger"
|
||||
"github.com/Azure/go-autorest/tracing"
|
||||
)
|
||||
|
||||
// there is one sender per TLS renegotiation type, i.e. count of tls.RenegotiationSupport enums
|
||||
const defaultSendersCount = 3
|
||||
|
||||
type defaultSender struct {
|
||||
sender Sender
|
||||
init *sync.Once
|
||||
}
|
||||
|
||||
// each type of sender will be created on demand in sender()
|
||||
var defaultSenders [defaultSendersCount]defaultSender
|
||||
|
||||
func init() {
|
||||
for i := 0; i < defaultSendersCount; i++ {
|
||||
defaultSenders[i].init = &sync.Once{}
|
||||
}
|
||||
}
|
||||
|
||||
// used as a key type in context.WithValue()
|
||||
type ctxSendDecorators struct{}
|
||||
|
||||
@@ -107,26 +127,34 @@ func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*ht
|
||||
}
|
||||
|
||||
func sender(renengotiation tls.RenegotiationSupport) Sender {
|
||||
// Use behaviour compatible with DefaultTransport, but require TLS minimum version.
|
||||
defaultTransport := http.DefaultTransport.(*http.Transport)
|
||||
transport := &http.Transport{
|
||||
Proxy: defaultTransport.Proxy,
|
||||
DialContext: defaultTransport.DialContext,
|
||||
MaxIdleConns: defaultTransport.MaxIdleConns,
|
||||
IdleConnTimeout: defaultTransport.IdleConnTimeout,
|
||||
TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout,
|
||||
ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
|
||||
TLSClientConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
Renegotiation: renengotiation,
|
||||
},
|
||||
}
|
||||
var roundTripper http.RoundTripper = transport
|
||||
if tracing.IsEnabled() {
|
||||
roundTripper = tracing.NewTransport(transport)
|
||||
}
|
||||
j, _ := cookiejar.New(nil)
|
||||
return &http.Client{Jar: j, Transport: roundTripper}
|
||||
// note that we can't init defaultSenders in init() since it will
|
||||
// execute before calling code has had a chance to enable tracing
|
||||
defaultSenders[renengotiation].init.Do(func() {
|
||||
// copied from http.DefaultTransport with a TLS minimum version.
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
Renegotiation: renengotiation,
|
||||
},
|
||||
}
|
||||
var roundTripper http.RoundTripper = transport
|
||||
if tracing.IsEnabled() {
|
||||
roundTripper = tracing.NewTransport(transport)
|
||||
}
|
||||
j, _ := cookiejar.New(nil)
|
||||
defaultSenders[renengotiation].sender = &http.Client{Jar: j, Transport: roundTripper}
|
||||
})
|
||||
return defaultSenders[renengotiation].sender
|
||||
}
|
||||
|
||||
// AfterDelay returns a SendDecorator that delays for the passed time.Duration before
|
||||
@@ -248,6 +276,7 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
|
||||
if err == nil {
|
||||
return resp, err
|
||||
}
|
||||
logger.Instance.Writef(logger.LogError, "DoRetryForAttempts: received error for attempt %d: %v\n", attempt+1, err)
|
||||
if !DelayForBackoff(backoff, attempt, r.Context().Done()) {
|
||||
return nil, r.Context().Err()
|
||||
}
|
||||
@@ -257,6 +286,12 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
|
||||
}
|
||||
}
|
||||
|
||||
// Count429AsRetry indicates that a 429 response should be included as a retry attempt.
|
||||
var Count429AsRetry = true
|
||||
|
||||
// Max429Delay is the maximum duration to wait between retries on a 429 if no Retry-After header was received.
|
||||
var Max429Delay time.Duration
|
||||
|
||||
// DoRetryForStatusCodes returns a SendDecorator that retries for specified statusCodes for up to the specified
|
||||
// number of attempts, exponentially backing off between requests using the supplied backoff
|
||||
// time.Duration (which may be zero). Retrying may be canceled by cancelling the context on the http.Request.
|
||||
@@ -264,7 +299,7 @@ func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator {
|
||||
func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
return doRetryForStatusCodesImpl(s, r, false, attempts, backoff, 0, codes...)
|
||||
return doRetryForStatusCodesImpl(s, r, Count429AsRetry, attempts, backoff, 0, codes...)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -276,7 +311,7 @@ func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) Se
|
||||
func DoRetryForStatusCodesWithCap(attempts int, backoff, cap time.Duration, codes ...int) SendDecorator {
|
||||
return func(s Sender) Sender {
|
||||
return SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||
return doRetryForStatusCodesImpl(s, r, true, attempts, backoff, cap, codes...)
|
||||
return doRetryForStatusCodesImpl(s, r, Count429AsRetry, attempts, backoff, cap, codes...)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -296,12 +331,14 @@ func doRetryForStatusCodesImpl(s Sender, r *http.Request, count429 bool, attempt
|
||||
if err == nil && !ResponseHasStatusCode(resp, codes...) || IsTokenRefreshError(err) {
|
||||
return resp, err
|
||||
}
|
||||
if err != nil {
|
||||
logger.Instance.Writef(logger.LogError, "DoRetryForStatusCodes: received error for attempt %d: %v\n", attempt+1, err)
|
||||
}
|
||||
delayed := DelayWithRetryAfter(resp, r.Context().Done())
|
||||
// enforce a 2 minute cap between requests when 429 status codes are
|
||||
// not going to be counted as an attempt and when the cap is 0.
|
||||
// this should only happen in the absence of a retry-after header.
|
||||
if !count429 && cap == 0 {
|
||||
cap = 2 * time.Minute
|
||||
// if this was a 429 set the delay cap as specified.
|
||||
// applicable only in the absence of a retry-after header.
|
||||
if resp != nil && resp.StatusCode == http.StatusTooManyRequests {
|
||||
cap = Max429Delay
|
||||
}
|
||||
if !delayed && !DelayForBackoffWithCap(backoff, cap, delayCount, r.Context().Done()) {
|
||||
return resp, r.Context().Err()
|
||||
@@ -363,6 +400,7 @@ func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator {
|
||||
if err == nil {
|
||||
return resp, err
|
||||
}
|
||||
logger.Instance.Writef(logger.LogError, "DoRetryForDuration: received error for attempt %d: %v\n", attempt+1, err)
|
||||
if !DelayForBackoff(backoff, attempt, r.Context().Done()) {
|
||||
return nil, r.Context().Err()
|
||||
}
|
||||
@@ -410,6 +448,7 @@ func DelayForBackoffWithCap(backoff, cap time.Duration, attempt int, cancel <-ch
|
||||
if cap > 0 && d > cap {
|
||||
d = cap
|
||||
}
|
||||
logger.Instance.Writef(logger.LogInfo, "DelayForBackoffWithCap: sleeping for %s\n", d)
|
||||
select {
|
||||
case <-time.After(d):
|
||||
return true
|
||||
|
||||
21
vendor/github.com/Azure/go-autorest/autorest/utility.go
generated
vendored
21
vendor/github.com/Azure/go-autorest/autorest/utility.go
generated
vendored
@@ -26,8 +26,6 @@ import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
)
|
||||
|
||||
// EncodedAs is a series of constants specifying various data encodings
|
||||
@@ -207,18 +205,6 @@ func ChangeToGet(req *http.Request) *http.Request {
|
||||
return req
|
||||
}
|
||||
|
||||
// IsTokenRefreshError returns true if the specified error implements the TokenRefreshError
|
||||
// interface. If err is a DetailedError it will walk the chain of Original errors.
|
||||
func IsTokenRefreshError(err error) bool {
|
||||
if _, ok := err.(adal.TokenRefreshError); ok {
|
||||
return true
|
||||
}
|
||||
if de, ok := err.(DetailedError); ok {
|
||||
return IsTokenRefreshError(de.Original)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTemporaryNetworkError returns true if the specified error is a temporary network error or false
|
||||
// if it's not. If the error doesn't implement the net.Error interface the return value is true.
|
||||
func IsTemporaryNetworkError(err error) bool {
|
||||
@@ -237,3 +223,10 @@ func DrainResponseBody(resp *http.Response) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setHeader(r *http.Request, key, value string) {
|
||||
if r.Header == nil {
|
||||
r.Header = make(http.Header)
|
||||
}
|
||||
r.Header.Set(key, value)
|
||||
}
|
||||
|
||||
30
vendor/github.com/Azure/go-autorest/autorest/utility_1.13.go
generated
vendored
Normal file
30
vendor/github.com/Azure/go-autorest/autorest/utility_1.13.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
//go:build go1.13
|
||||
// +build go1.13
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// 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 autorest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
)
|
||||
|
||||
// IsTokenRefreshError returns true if the specified error implements the TokenRefreshError interface.
|
||||
func IsTokenRefreshError(err error) bool {
|
||||
var tre adal.TokenRefreshError
|
||||
return errors.As(err, &tre)
|
||||
}
|
||||
32
vendor/github.com/Azure/go-autorest/autorest/utility_legacy.go
generated
vendored
Normal file
32
vendor/github.com/Azure/go-autorest/autorest/utility_legacy.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
//go:build !go1.13
|
||||
// +build !go1.13
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// 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 autorest
|
||||
|
||||
import "github.com/Azure/go-autorest/autorest/adal"
|
||||
|
||||
// IsTokenRefreshError returns true if the specified error implements the TokenRefreshError
|
||||
// interface. If err is a DetailedError it will walk the chain of Original errors.
|
||||
func IsTokenRefreshError(err error) bool {
|
||||
if _, ok := err.(adal.TokenRefreshError); ok {
|
||||
return true
|
||||
}
|
||||
if de, ok := err.(DetailedError); ok {
|
||||
return IsTokenRefreshError(de.Original)
|
||||
}
|
||||
return false
|
||||
}
|
||||
2
vendor/github.com/Azure/go-autorest/autorest/version.go
generated
vendored
2
vendor/github.com/Azure/go-autorest/autorest/version.go
generated
vendored
@@ -19,7 +19,7 @@ import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
const number = "v13.4.0"
|
||||
const number = "v14.2.1"
|
||||
|
||||
var (
|
||||
userAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s",
|
||||
|
||||
105
vendor/github.com/Azure/go-autorest/azure-pipelines.yml
generated
vendored
Normal file
105
vendor/github.com/Azure/go-autorest/azure-pipelines.yml
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
variables:
|
||||
GOPATH: '$(system.defaultWorkingDirectory)/work'
|
||||
sdkPath: '$(GOPATH)/src/github.com/$(build.repository.name)'
|
||||
|
||||
jobs:
|
||||
- job: 'goautorest'
|
||||
displayName: 'Run go-autorest CI Checks'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_Go113:
|
||||
vm.image: 'ubuntu-18.04'
|
||||
go.version: '1.13'
|
||||
Linux_Go114:
|
||||
vm.image: 'ubuntu-18.04'
|
||||
go.version: '1.14'
|
||||
|
||||
pool:
|
||||
vmImage: '$(vm.image)'
|
||||
|
||||
steps:
|
||||
- task: GoTool@0
|
||||
inputs:
|
||||
version: '$(go.version)'
|
||||
displayName: "Select Go Version"
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
mkdir -p '$(GOPATH)/bin'
|
||||
mkdir -p '$(sdkPath)'
|
||||
shopt -s extglob
|
||||
mv !(work) '$(sdkPath)'
|
||||
echo '##vso[task.prependpath]$(GOPATH)/bin'
|
||||
displayName: 'Create Go Workspace'
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
curl -sSL https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
dep ensure -v
|
||||
go install ./vendor/golang.org/x/lint/golint
|
||||
go get github.com/jstemmer/go-junit-report
|
||||
go get github.com/axw/gocov/gocov
|
||||
go get github.com/AlekSi/gocov-xml
|
||||
go get -u github.com/matm/gocov-html
|
||||
workingDirectory: '$(sdkPath)'
|
||||
displayName: 'Install Dependencies'
|
||||
|
||||
- script: |
|
||||
go vet ./autorest/...
|
||||
go vet ./logger/...
|
||||
go vet ./tracing/...
|
||||
workingDirectory: '$(sdkPath)'
|
||||
displayName: 'Vet'
|
||||
|
||||
- script: |
|
||||
go build -v ./autorest/...
|
||||
go build -v ./logger/...
|
||||
go build -v ./tracing/...
|
||||
workingDirectory: '$(sdkPath)'
|
||||
displayName: 'Build'
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
go test -race -v -coverprofile=coverage.txt -covermode atomic ./autorest/... ./logger/... ./tracing/... 2>&1 | go-junit-report > report.xml
|
||||
gocov convert coverage.txt > coverage.json
|
||||
gocov-xml < coverage.json > coverage.xml
|
||||
gocov-html < coverage.json > coverage.html
|
||||
workingDirectory: '$(sdkPath)'
|
||||
displayName: 'Run Tests'
|
||||
|
||||
- script: grep -L -r --include *.go --exclude-dir vendor -P "Copyright (\d{4}|\(c\)) Microsoft" ./ | tee >&2
|
||||
workingDirectory: '$(sdkPath)'
|
||||
displayName: 'Copyright Header Check'
|
||||
failOnStderr: true
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- script: |
|
||||
gofmt -s -l -w ./autorest/. >&2
|
||||
gofmt -s -l -w ./logger/. >&2
|
||||
gofmt -s -l -w ./tracing/. >&2
|
||||
workingDirectory: '$(sdkPath)'
|
||||
displayName: 'Format Check'
|
||||
failOnStderr: true
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- script: |
|
||||
golint ./autorest/... >&2
|
||||
golint ./logger/... >&2
|
||||
golint ./tracing/... >&2
|
||||
workingDirectory: '$(sdkPath)'
|
||||
displayName: 'Linter Check'
|
||||
failOnStderr: true
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testRunner: JUnit
|
||||
testResultsFiles: $(sdkPath)/report.xml
|
||||
failTaskOnFailedTests: true
|
||||
|
||||
- task: PublishCodeCoverageResults@1
|
||||
inputs:
|
||||
codeCoverageTool: Cobertura
|
||||
summaryFileLocation: $(sdkPath)/coverage.xml
|
||||
additionalCodeCoverageFiles: $(sdkPath)/coverage.html
|
||||
18
vendor/github.com/Azure/go-autorest/doc.go
generated
vendored
Normal file
18
vendor/github.com/Azure/go-autorest/doc.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Package go-autorest provides an HTTP request client for use with Autorest-generated API client packages.
|
||||
*/
|
||||
package go_autorest
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// 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.
|
||||
24
vendor/github.com/Azure/go-autorest/logger/go_mod_tidy_hack.go
generated
vendored
Normal file
24
vendor/github.com/Azure/go-autorest/logger/go_mod_tidy_hack.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build modhack
|
||||
|
||||
package logger
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// 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, and the github.com/Azure/go-autorest import, won't actually become part of
|
||||
// the resultant binary.
|
||||
|
||||
// Necessary for safely adding multi-module repo.
|
||||
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
|
||||
import _ "github.com/Azure/go-autorest"
|
||||
9
vendor/github.com/Azure/go-autorest/logger/logger.go
generated
vendored
9
vendor/github.com/Azure/go-autorest/logger/logger.go
generated
vendored
@@ -55,6 +55,10 @@ const (
|
||||
|
||||
// LogDebug tells a logger to log all LogDebug, LogInfo, LogWarning, LogError, LogPanic and LogFatal entries passed to it.
|
||||
LogDebug
|
||||
|
||||
// LogAuth is a special case of LogDebug, it tells a logger to also log the body of an authentication request and response.
|
||||
// NOTE: this can disclose sensitive information, use with care.
|
||||
LogAuth
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -65,6 +69,7 @@ const (
|
||||
logWarning = "WARNING"
|
||||
logInfo = "INFO"
|
||||
logDebug = "DEBUG"
|
||||
logAuth = "AUTH"
|
||||
logUnknown = "UNKNOWN"
|
||||
)
|
||||
|
||||
@@ -83,6 +88,8 @@ func ParseLevel(s string) (lt LevelType, err error) {
|
||||
lt = LogInfo
|
||||
case logDebug:
|
||||
lt = LogDebug
|
||||
case logAuth:
|
||||
lt = LogAuth
|
||||
default:
|
||||
err = fmt.Errorf("bad log level '%s'", s)
|
||||
}
|
||||
@@ -106,6 +113,8 @@ func (lt LevelType) String() string {
|
||||
return logInfo
|
||||
case LogDebug:
|
||||
return logDebug
|
||||
case LogAuth:
|
||||
return logAuth
|
||||
default:
|
||||
return logUnknown
|
||||
}
|
||||
|
||||
24
vendor/github.com/Azure/go-autorest/tracing/go_mod_tidy_hack.go
generated
vendored
Normal file
24
vendor/github.com/Azure/go-autorest/tracing/go_mod_tidy_hack.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build modhack
|
||||
|
||||
package tracing
|
||||
|
||||
// Copyright 2017 Microsoft Corporation
|
||||
//
|
||||
// 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, and the github.com/Azure/go-autorest import, won't actually become part of
|
||||
// the resultant binary.
|
||||
|
||||
// Necessary for safely adding multi-module repo.
|
||||
// See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository
|
||||
import _ "github.com/Azure/go-autorest"
|
||||
200
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/LICENSE
generated
vendored
Normal file
200
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2016-present Datadog, Inc.
|
||||
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.
|
||||
85
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/cache.go
generated
vendored
Normal file
85
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/cache.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
package obfuscate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/dgraph-io/ristretto"
|
||||
)
|
||||
|
||||
// measuredCache is a wrapper on top of *ristretto.Cache which additionally
|
||||
// sends metrics (hits and misses) every 10 seconds.
|
||||
type measuredCache struct {
|
||||
*ristretto.Cache
|
||||
|
||||
// close allows sending shutdown notification.
|
||||
close chan struct{}
|
||||
statsd StatsClient
|
||||
}
|
||||
|
||||
// Close gracefully closes the cache when active.
|
||||
func (c *measuredCache) Close() {
|
||||
if c.Cache == nil {
|
||||
return
|
||||
}
|
||||
c.close <- struct{}{}
|
||||
<-c.close
|
||||
}
|
||||
|
||||
func (c *measuredCache) statsLoop() {
|
||||
defer func() {
|
||||
c.close <- struct{}{}
|
||||
}()
|
||||
tick := time.NewTicker(10 * time.Second)
|
||||
defer tick.Stop()
|
||||
mx := c.Cache.Metrics
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
c.statsd.Gauge("datadog.trace_agent.ofuscation.sql_cache.hits", float64(mx.Hits()), nil, 1) //nolint:errcheck
|
||||
c.statsd.Gauge("datadog.trace_agent.ofuscation.sql_cache.misses", float64(mx.Misses()), nil, 1) //nolint:errcheck
|
||||
case <-c.close:
|
||||
c.Cache.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type cacheOptions struct {
|
||||
On bool
|
||||
Statsd StatsClient
|
||||
}
|
||||
|
||||
// newMeasuredCache returns a new measuredCache.
|
||||
func newMeasuredCache(opts cacheOptions) *measuredCache {
|
||||
if !opts.On {
|
||||
// a nil *ristretto.Cache is a no-op cache
|
||||
return &measuredCache{}
|
||||
}
|
||||
cfg := &ristretto.Config{
|
||||
// We know that the maximum allowed resource length is 5K. This means that
|
||||
// in 5MB we can store a minimum of 1000 queries.
|
||||
MaxCost: 5000000,
|
||||
|
||||
// An appromixated worst-case scenario when the cache is filled with small
|
||||
// queries averaged as being of length 11 ("LOCK TABLES"), we would be able
|
||||
// to fit 476K of them into 5MB of cost.
|
||||
//
|
||||
// We average it to 500K and multiply 10x as the documentation recommends.
|
||||
NumCounters: 500000 * 10,
|
||||
|
||||
BufferItems: 64, // default recommended value
|
||||
Metrics: true, // enable hit/miss counters
|
||||
}
|
||||
cache, err := ristretto.NewCache(cfg)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("Error starting obfuscator query cache: %v", err))
|
||||
}
|
||||
c := measuredCache{
|
||||
close: make(chan struct{}),
|
||||
statsd: opts.Statsd,
|
||||
Cache: cache,
|
||||
}
|
||||
go c.statsLoop()
|
||||
return &c
|
||||
}
|
||||
212
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/credit_cards.go
generated
vendored
Normal file
212
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/credit_cards.go
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
package obfuscate
|
||||
|
||||
// IsCardNumber checks if b could be a credit card number by checking the digit count and IIN prefix.
|
||||
// If validateLuhn is true, the Luhn checksum is also applied to potential candidates.
|
||||
func IsCardNumber(b string, validateLuhn bool) (ok bool) {
|
||||
//
|
||||
// Just credit card numbers for now, based on:
|
||||
// • https://baymard.com/checkout-usability/credit-card-patterns
|
||||
// • https://www.regular-expressions.info/creditcard.html
|
||||
//
|
||||
if len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
if len(b) < 12 {
|
||||
// fast path: can not be a credit card
|
||||
return false
|
||||
}
|
||||
if b[0] != ' ' && b[0] != '-' && (b[0] < '0' || b[0] > '9') {
|
||||
// fast path: only valid characters are 0-9, space (" ") and dash("-")
|
||||
return false
|
||||
}
|
||||
prefix := 0 // holds up to b[:6] digits as a numeric value (for example []byte{"523"} becomes int(523)) for checking prefixes
|
||||
count := 0 // counts digits encountered
|
||||
foundPrefix := false // reports whether we've detected a valid prefix
|
||||
recdigit := func(_ byte) {} // callback on each found digit; no-op by default (we only need this for Luhn)
|
||||
if validateLuhn {
|
||||
// we need Luhn checksum validation, so we have to take additional action
|
||||
// and record all digits found
|
||||
buf := make([]byte, 0, len(b))
|
||||
recdigit = func(b byte) { buf = append(buf, b) }
|
||||
defer func() {
|
||||
if !ok {
|
||||
// if isCardNumber returned false, it means that b can not be
|
||||
// a credit card number
|
||||
return
|
||||
}
|
||||
// potentially a credit card number, run the Luhn checksum
|
||||
ok = luhnValid(buf)
|
||||
}()
|
||||
}
|
||||
loop:
|
||||
for i := range b {
|
||||
// We traverse and search b for a valid IIN credit card prefix based
|
||||
// on the digits found, ignoring spaces and dashes.
|
||||
// Source: https://www.regular-expressions.info/creditcard.html
|
||||
switch b[i] {
|
||||
case ' ', '-':
|
||||
// ignore space (' ') and dash ('-')
|
||||
continue loop
|
||||
}
|
||||
if b[i] < '0' || b[i] > '9' {
|
||||
// not a 0 to 9 digit; can not be a credit card number; abort
|
||||
return false
|
||||
}
|
||||
count++
|
||||
recdigit(b[i])
|
||||
if !foundPrefix {
|
||||
// we have not yet found a valid prefix so we convert the digits
|
||||
// that we have so far into a numeric value:
|
||||
prefix = prefix*10 + (int(b[i]) - '0')
|
||||
maybe, yes := validCardPrefix(prefix)
|
||||
if yes {
|
||||
// we've found a valid prefix; continue counting
|
||||
foundPrefix = true
|
||||
} else if !maybe {
|
||||
// this is not a valid prefix and we should not continue looking
|
||||
return false
|
||||
}
|
||||
}
|
||||
if count > 16 {
|
||||
// too many digits
|
||||
return false
|
||||
}
|
||||
}
|
||||
if count < 12 {
|
||||
// too few digits
|
||||
return false
|
||||
}
|
||||
return foundPrefix
|
||||
}
|
||||
|
||||
// luhnValid checks that the number represented in the given string validates the Luhn Checksum algorithm.
|
||||
// str is expected to contain exclusively digits at all positions.
|
||||
//
|
||||
// See:
|
||||
// • https://en.wikipedia.org/wiki/Luhn_algorithm
|
||||
// • https://dev.to/shiraazm/goluhn-a-simple-library-for-generating-calculating-and-verifying-luhn-numbers-588j
|
||||
//
|
||||
func luhnValid(str []byte) bool {
|
||||
var (
|
||||
sum int
|
||||
alt bool
|
||||
)
|
||||
n := len(str)
|
||||
for i := n - 1; i > -1; i-- {
|
||||
if str[i] < '0' || str[i] > '9' {
|
||||
return false // not a number!
|
||||
}
|
||||
mod := int(str[i] - 0x30) // convert byte to int
|
||||
if alt {
|
||||
mod *= 2
|
||||
if mod > 9 {
|
||||
mod = (mod % 10) + 1
|
||||
}
|
||||
}
|
||||
alt = !alt
|
||||
sum += mod
|
||||
}
|
||||
return sum%10 == 0
|
||||
}
|
||||
|
||||
// validCardPrefix validates whether b is a valid card prefix. Maybe returns true if
|
||||
// the prefix could be an IIN once more digits are revealed and yes reports whether
|
||||
// b is a fully valid IIN.
|
||||
//
|
||||
// If yes is false and maybe is false, there is no reason to continue searching. The
|
||||
// prefix is invalid.
|
||||
//
|
||||
// IMPORTANT: If adding new prefixes to this algorithm, make sure that you update
|
||||
// the "maybe" clauses above, in the shorter prefixes than the one you are adding.
|
||||
// This refers to the cases which return true, false.
|
||||
//
|
||||
// TODO(x): this whole code could be code generated from a prettier data structure.
|
||||
// Ultimately, it could even be user-configurable.
|
||||
func validCardPrefix(n int) (maybe, yes bool) {
|
||||
// Validates IIN prefix possibilities
|
||||
// Source: https://www.regular-expressions.info/creditcard.html
|
||||
if n > 699999 {
|
||||
// too long for any known prefix; stop looking
|
||||
return false, false
|
||||
}
|
||||
if n < 10 {
|
||||
switch n {
|
||||
case 1, 4:
|
||||
// 1 & 4 are valid IIN
|
||||
return false, true
|
||||
case 2, 3, 5, 6:
|
||||
// 2, 3, 5, 6 could be the start of valid IIN
|
||||
return true, false
|
||||
default:
|
||||
// invalid IIN
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
if n < 100 {
|
||||
if (n >= 34 && n <= 39) ||
|
||||
(n >= 51 && n <= 55) ||
|
||||
n == 62 ||
|
||||
n == 65 {
|
||||
// 34-39, 51-55, 62, 65 are valid IIN
|
||||
return false, true
|
||||
}
|
||||
if n == 30 || n == 63 || n == 64 || n == 35 || n == 50 || n == 60 ||
|
||||
(n >= 22 && n <= 27) || (n >= 56 && n <= 58) || (n >= 60 && n <= 69) {
|
||||
// 30, 63, 64, 35, 50, 60, 22-27, 56-58, 60-69 may end up as valid IIN
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
if n < 1000 {
|
||||
if (n >= 300 && n <= 305) ||
|
||||
(n >= 644 && n <= 649) ||
|
||||
n == 309 ||
|
||||
n == 636 {
|
||||
// 300‑305, 309, 636, 644‑649 are valid IIN
|
||||
return false, true
|
||||
}
|
||||
if (n >= 352 && n <= 358) || n == 501 || n == 601 ||
|
||||
(n >= 222 && n <= 272) || (n >= 500 && n <= 509) ||
|
||||
(n >= 560 && n <= 589) || (n >= 600 && n <= 699) {
|
||||
// 352-358, 501, 601, 222-272, 500-509, 560-589, 600-699 may be a 4 or 6 digit IIN prefix
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
if n < 10000 {
|
||||
if (n >= 3528 && n <= 3589) ||
|
||||
n == 5019 ||
|
||||
n == 6011 {
|
||||
// 3528‑3589, 5019, 6011 are valid IINs
|
||||
return false, true
|
||||
}
|
||||
if (n >= 2221 && n <= 2720) || (n >= 5000 && n <= 5099) ||
|
||||
(n >= 5600 && n <= 5899) || (n >= 6000 && n <= 6999) {
|
||||
// maybe a 6-digit IIN
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
if n < 100000 {
|
||||
if (n >= 22210 && n <= 27209) ||
|
||||
(n >= 50000 && n <= 50999) ||
|
||||
(n >= 56000 && n <= 58999) ||
|
||||
(n >= 60000 && n <= 69999) {
|
||||
// maybe a 6-digit IIN
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
if n < 1000000 {
|
||||
if (n >= 222100 && n <= 272099) ||
|
||||
(n >= 500000 && n <= 509999) ||
|
||||
(n >= 560000 && n <= 589999) ||
|
||||
(n >= 600000 && n <= 699999) {
|
||||
// 222100‑272099, 500000‑509999, 560000‑589999, 600000‑699999 are valid IIN
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
// unknown IIN
|
||||
return false, false
|
||||
}
|
||||
50
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/http.go
generated
vendored
Normal file
50
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/http.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
package obfuscate
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ObfuscateURLString obfuscates the given URL. It must be a valid URL and at least one
|
||||
// HTTP obfuscation option must be enabled at Obfuscator instantiation time.
|
||||
func (o *Obfuscator) ObfuscateURLString(val string) string {
|
||||
if !o.opts.HTTP.RemoveQueryString && !o.opts.HTTP.RemovePathDigits {
|
||||
// nothing to do
|
||||
return val
|
||||
}
|
||||
u, err := url.Parse(val)
|
||||
if err != nil {
|
||||
// should not happen for valid URLs, but better obfuscate everything
|
||||
// rather than expose sensitive information when this option is on.
|
||||
return "?"
|
||||
}
|
||||
if o.opts.HTTP.RemoveQueryString && u.RawQuery != "" {
|
||||
u.ForceQuery = true // add the '?'
|
||||
u.RawQuery = ""
|
||||
}
|
||||
if o.opts.HTTP.RemovePathDigits {
|
||||
segs := strings.Split(u.Path, "/")
|
||||
var changed bool
|
||||
for i, seg := range segs {
|
||||
for _, ch := range []byte(seg) {
|
||||
if ch >= '0' && ch <= '9' {
|
||||
// we can not set the question mark directly here because the url
|
||||
// package will escape it into %3F, so we use this placeholder and
|
||||
// replace it further down.
|
||||
segs[i] = "/REDACTED/"
|
||||
changed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
u.Path = strings.Join(segs, "/")
|
||||
}
|
||||
}
|
||||
return strings.Replace(u.String(), "/REDACTED/", "?", -1)
|
||||
}
|
||||
201
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json.go
generated
vendored
Normal file
201
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json.go
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
package obfuscate
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ObfuscateMongoDBString obfuscates the given MongoDB JSON query.
|
||||
func (o *Obfuscator) ObfuscateMongoDBString(cmd string) string {
|
||||
return obfuscateJSONString(cmd, o.mongo)
|
||||
}
|
||||
|
||||
// ObfuscateElasticSearchString obfuscates the given ElasticSearch JSON query.
|
||||
func (o *Obfuscator) ObfuscateElasticSearchString(cmd string) string {
|
||||
return obfuscateJSONString(cmd, o.es)
|
||||
}
|
||||
|
||||
// obfuscateJSONString obfuscates the given span's tag using the given obfuscator. If the obfuscator is
|
||||
// nil it is considered disabled.
|
||||
func obfuscateJSONString(cmd string, obfuscator *jsonObfuscator) string {
|
||||
if obfuscator == nil || cmd == "" {
|
||||
// obfuscator is disabled or string is empty
|
||||
return cmd
|
||||
}
|
||||
out, _ := obfuscator.obfuscate([]byte(cmd))
|
||||
// we should accept whatever the obfuscator returns, even if it's an error: a parsing
|
||||
// error simply means that the JSON was invalid, meaning that we've only obfuscated
|
||||
// as much of it as we could. It is safe to accept the output, even if partial.
|
||||
return out
|
||||
}
|
||||
|
||||
type jsonObfuscator struct {
|
||||
keepKeys map[string]bool // the values for these keys will not be obfuscated
|
||||
transformKeys map[string]bool // the values for these keys pass through the transformer
|
||||
transformer func(string) string
|
||||
|
||||
scan *scanner // scanner
|
||||
closures []bool // closure stack, true if object (e.g. {[{ => []bool{true, false, true})
|
||||
key bool // true if scanning a key
|
||||
|
||||
wiped bool // true if obfuscation string (`"?"`) was already written for current value
|
||||
keeping bool // true if not obfuscating
|
||||
transformingValue bool // true if collecting the next literal for transformation
|
||||
keepDepth int // the depth at which we've stopped obfuscating
|
||||
}
|
||||
|
||||
func newJSONObfuscator(cfg *JSONConfig, o *Obfuscator) *jsonObfuscator {
|
||||
keepValue := make(map[string]bool, len(cfg.KeepValues))
|
||||
for _, v := range cfg.KeepValues {
|
||||
keepValue[v] = true
|
||||
}
|
||||
var (
|
||||
transformKeys map[string]bool
|
||||
transformer func(string) string
|
||||
)
|
||||
if len(cfg.ObfuscateSQLValues) > 0 {
|
||||
transformer = sqlObfuscationTransformer(o)
|
||||
transformKeys = make(map[string]bool, len(cfg.ObfuscateSQLValues))
|
||||
for _, v := range cfg.ObfuscateSQLValues {
|
||||
transformKeys[v] = true
|
||||
}
|
||||
}
|
||||
return &jsonObfuscator{
|
||||
closures: []bool{},
|
||||
keepKeys: keepValue,
|
||||
transformKeys: transformKeys,
|
||||
transformer: transformer,
|
||||
scan: &scanner{},
|
||||
}
|
||||
}
|
||||
|
||||
func sqlObfuscationTransformer(o *Obfuscator) func(string) string {
|
||||
return func(s string) string {
|
||||
result, err := o.ObfuscateSQLString(s)
|
||||
if err != nil {
|
||||
o.log.Debugf("Failed to obfuscate SQL string '%s': %s", s, err.Error())
|
||||
// instead of returning an empty string we explicitly return an error string here within the result in order
|
||||
// to surface the problem clearly to the user
|
||||
return "Datadog-agent failed to obfuscate SQL string. Enable agent debug logs for more info."
|
||||
}
|
||||
return result.Query
|
||||
}
|
||||
}
|
||||
|
||||
// setKey verifies if we are currently scanning a key based on the current state
|
||||
// and updates the state accordingly. It must be called only after a closure or a
|
||||
// value scan has ended.
|
||||
func (p *jsonObfuscator) setKey() {
|
||||
n := len(p.closures)
|
||||
p.key = n == 0 || p.closures[n-1] // true if we are at top level or in an object
|
||||
p.wiped = false
|
||||
}
|
||||
|
||||
func (p *jsonObfuscator) obfuscate(data []byte) (string, error) {
|
||||
var out strings.Builder
|
||||
|
||||
keyBuf := make([]byte, 0, 10) // recording key token
|
||||
valBuf := make([]byte, 0, 10) // recording value
|
||||
|
||||
p.scan.reset()
|
||||
for _, c := range data {
|
||||
p.scan.bytes++
|
||||
op := p.scan.step(p.scan, c)
|
||||
depth := len(p.closures)
|
||||
switch op {
|
||||
case scanBeginObject:
|
||||
// object begins: {
|
||||
p.closures = append(p.closures, true)
|
||||
p.setKey()
|
||||
p.transformingValue = false
|
||||
|
||||
case scanBeginArray:
|
||||
// array begins: [
|
||||
p.closures = append(p.closures, false)
|
||||
p.setKey()
|
||||
p.transformingValue = false
|
||||
|
||||
case scanEndArray, scanEndObject:
|
||||
// array or object closing
|
||||
if n := len(p.closures) - 1; n > 0 {
|
||||
p.closures = p.closures[:n]
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case scanObjectValue, scanArrayValue:
|
||||
// done scanning value
|
||||
p.setKey()
|
||||
if p.transformingValue && p.transformer != nil {
|
||||
v, err := strconv.Unquote(string(valBuf))
|
||||
if err != nil {
|
||||
v = string(valBuf)
|
||||
}
|
||||
result := p.transformer(v)
|
||||
out.WriteByte('"')
|
||||
out.WriteString(result)
|
||||
out.WriteByte('"')
|
||||
p.transformingValue = false
|
||||
valBuf = valBuf[:0]
|
||||
} else if p.keeping && depth < p.keepDepth {
|
||||
p.keeping = false
|
||||
}
|
||||
|
||||
case scanBeginLiteral, scanContinue:
|
||||
// starting or continuing a literal
|
||||
if p.transformingValue {
|
||||
valBuf = append(valBuf, c)
|
||||
continue
|
||||
} else if p.key {
|
||||
// it's a key
|
||||
keyBuf = append(keyBuf, c)
|
||||
} else if !p.keeping {
|
||||
// it's a value we're not keeping
|
||||
if !p.wiped {
|
||||
out.Write([]byte(`"?"`))
|
||||
p.wiped = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
case scanObjectKey:
|
||||
// done scanning key
|
||||
k := strings.Trim(string(keyBuf), `"`)
|
||||
if !p.keeping && p.keepKeys[k] {
|
||||
// we should not obfuscate values of this key
|
||||
p.keeping = true
|
||||
p.keepDepth = depth + 1
|
||||
} else if !p.transformingValue && p.transformer != nil && p.transformKeys[k] {
|
||||
// the string value immediately following this key will be passed through the value transformer
|
||||
// if anything other than a literal is found then sql obfuscation is stopped and json obfuscation
|
||||
// proceeds as usual
|
||||
p.transformingValue = true
|
||||
}
|
||||
|
||||
keyBuf = keyBuf[:0]
|
||||
p.key = false
|
||||
|
||||
case scanSkipSpace:
|
||||
continue
|
||||
|
||||
case scanError:
|
||||
// we've encountered an error, mark that there might be more JSON
|
||||
// using the ellipsis and return whatever we've managed to obfuscate
|
||||
// thus far.
|
||||
out.Write([]byte("..."))
|
||||
return out.String(), p.scan.err
|
||||
}
|
||||
out.WriteByte(c)
|
||||
}
|
||||
if p.scan.eof() == scanError {
|
||||
// if an error occurred it's fine, simply add the ellipsis to indicate
|
||||
// that the input has been truncated.
|
||||
out.Write([]byte("..."))
|
||||
return out.String(), p.scan.err
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
565
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json_scanner.go
generated
vendored
Normal file
565
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/json_scanner.go
generated
vendored
Normal file
@@ -0,0 +1,565 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
// Copyright 2010 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.
|
||||
|
||||
//
|
||||
// The code that follows is copied from go/src/encoding/json/scanner.go
|
||||
// It may contain minor edits, such as allowing multiple JSON objects within
|
||||
// the same input string (see stateEndTop)
|
||||
//
|
||||
|
||||
package obfuscate
|
||||
|
||||
import "strconv"
|
||||
|
||||
// A SyntaxError is a description of a JSON syntax error.
|
||||
type SyntaxError struct {
|
||||
msg string // description of error
|
||||
Offset int64 // error occurred after reading Offset bytes
|
||||
}
|
||||
|
||||
func (e *SyntaxError) Error() string { return e.msg }
|
||||
|
||||
// A scanner is a JSON scanning state machine.
|
||||
// Callers call scan.reset() and then pass bytes in one at a time
|
||||
// by calling scan.step(&scan, c) for each byte.
|
||||
// The return value, referred to as an opcode, tells the
|
||||
// caller about significant parsing events like beginning
|
||||
// and ending literals, objects, and arrays, so that the
|
||||
// caller can follow along if it wishes.
|
||||
// The return value scanEnd indicates that a single top-level
|
||||
// JSON value has been completed, *before* the byte that
|
||||
// just got passed in. (The indication must be delayed in order
|
||||
// to recognize the end of numbers: is 123 a whole value or
|
||||
// the beginning of 12345e+6?).
|
||||
type scanner struct {
|
||||
// The step is a func to be called to execute the next transition.
|
||||
// Also tried using an integer constant and a single func
|
||||
// with a switch, but using the func directly was 10% faster
|
||||
// on a 64-bit Mac Mini, and it's nicer to read.
|
||||
step func(*scanner, byte) int
|
||||
|
||||
// Reached end of top-level value.
|
||||
endTop bool
|
||||
|
||||
// Stack of what we're in the middle of - array values, object keys, object values.
|
||||
parseState []int
|
||||
|
||||
// Error that happened, if any.
|
||||
err error
|
||||
|
||||
// 1-byte redo (see undo method)
|
||||
redo bool
|
||||
|
||||
// total bytes consumed, updated by decoder.Decode
|
||||
bytes int64
|
||||
}
|
||||
|
||||
// These values are returned by the state transition functions
|
||||
// assigned to scanner.state and the method scanner.eof.
|
||||
// They give details about the current state of the scan that
|
||||
// callers might be interested to know about.
|
||||
// It is okay to ignore the return value of any particular
|
||||
// call to scanner.state: if one call returns scanError,
|
||||
// every subsequent call will return scanError too.
|
||||
const (
|
||||
// Continue.
|
||||
scanContinue = iota // uninteresting byte
|
||||
scanBeginLiteral // end implied by next result != scanContinue
|
||||
scanBeginObject // begin object
|
||||
scanObjectKey // just finished object key (string)
|
||||
scanObjectValue // just finished non-last object value
|
||||
scanEndObject // end object (implies scanObjectValue if possible)
|
||||
scanBeginArray // begin array
|
||||
scanArrayValue // just finished array value
|
||||
scanEndArray // end array (implies scanArrayValue if possible)
|
||||
scanSkipSpace // space byte; can skip; known to be last "continue" result
|
||||
|
||||
// Stop.
|
||||
scanEnd // top-level value ended *before* this byte; known to be first "stop" result
|
||||
scanError // hit an error, scanner.err.
|
||||
)
|
||||
|
||||
// These values are stored in the parseState stack.
|
||||
// They give the current state of a composite value
|
||||
// being scanned. If the parser is inside a nested value
|
||||
// the parseState describes the nested state, outermost at entry 0.
|
||||
const (
|
||||
parseObjectKey = iota // parsing object key (before colon)
|
||||
parseObjectValue // parsing object value (after colon)
|
||||
parseArrayValue // parsing array value
|
||||
)
|
||||
|
||||
// reset prepares the scanner for use.
|
||||
// It must be called before calling s.step.
|
||||
func (s *scanner) reset() {
|
||||
s.step = stateBeginValue
|
||||
s.parseState = s.parseState[0:0]
|
||||
s.err = nil
|
||||
s.redo = false
|
||||
s.endTop = false
|
||||
}
|
||||
|
||||
// eof tells the scanner that the end of input has been reached.
|
||||
// It returns a scan status just as s.step does.
|
||||
func (s *scanner) eof() int {
|
||||
if s.err != nil {
|
||||
return scanError
|
||||
}
|
||||
if s.endTop {
|
||||
return scanEnd
|
||||
}
|
||||
s.step(s, ' ')
|
||||
if s.endTop {
|
||||
return scanEnd
|
||||
}
|
||||
if s.err == nil {
|
||||
s.err = &SyntaxError{"unexpected end of JSON input", s.bytes}
|
||||
}
|
||||
return scanError
|
||||
}
|
||||
|
||||
// pushParseState pushes a new parse state p onto the parse stack.
|
||||
func (s *scanner) pushParseState(p int) {
|
||||
s.parseState = append(s.parseState, p)
|
||||
}
|
||||
|
||||
// popParseState pops a parse state (already obtained) off the stack
|
||||
// and updates s.step accordingly.
|
||||
func (s *scanner) popParseState() {
|
||||
n := len(s.parseState) - 1
|
||||
s.parseState = s.parseState[0:n]
|
||||
s.redo = false
|
||||
if n == 0 {
|
||||
s.step = stateEndTop
|
||||
s.endTop = true
|
||||
} else {
|
||||
s.step = stateEndValue
|
||||
}
|
||||
}
|
||||
|
||||
func isSpace(c byte) bool {
|
||||
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
|
||||
}
|
||||
|
||||
// stateBeginValueOrEmpty is the state after reading `[`.
|
||||
func stateBeginValueOrEmpty(s *scanner, c byte) int {
|
||||
if c <= ' ' && isSpace(c) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
if c == ']' {
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
return stateBeginValue(s, c)
|
||||
}
|
||||
|
||||
// stateBeginValue is the state at the beginning of the input.
|
||||
func stateBeginValue(s *scanner, c byte) int {
|
||||
if c <= ' ' && isSpace(c) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
switch c {
|
||||
case '{':
|
||||
s.step = stateBeginStringOrEmpty
|
||||
s.pushParseState(parseObjectKey)
|
||||
return scanBeginObject
|
||||
case '[':
|
||||
s.step = stateBeginValueOrEmpty
|
||||
s.pushParseState(parseArrayValue)
|
||||
return scanBeginArray
|
||||
case '"':
|
||||
s.step = stateInString
|
||||
return scanBeginLiteral
|
||||
case '-':
|
||||
s.step = stateNeg
|
||||
return scanBeginLiteral
|
||||
case '0': // beginning of 0.123
|
||||
s.step = state0
|
||||
return scanBeginLiteral
|
||||
case 't': // beginning of true
|
||||
s.step = stateT
|
||||
return scanBeginLiteral
|
||||
case 'f': // beginning of false
|
||||
s.step = stateF
|
||||
return scanBeginLiteral
|
||||
case 'n': // beginning of null
|
||||
s.step = stateN
|
||||
return scanBeginLiteral
|
||||
}
|
||||
if '1' <= c && c <= '9' { // beginning of 1234.5
|
||||
s.step = state1
|
||||
return scanBeginLiteral
|
||||
}
|
||||
return s.error(c, "looking for beginning of value")
|
||||
}
|
||||
|
||||
// stateBeginStringOrEmpty is the state after reading `{`.
|
||||
func stateBeginStringOrEmpty(s *scanner, c byte) int {
|
||||
if c <= ' ' && isSpace(c) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
if c == '}' {
|
||||
n := len(s.parseState)
|
||||
s.parseState[n-1] = parseObjectValue
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
return stateBeginString(s, c)
|
||||
}
|
||||
|
||||
// stateBeginString is the state after reading `{"key": value,`.
|
||||
func stateBeginString(s *scanner, c byte) int {
|
||||
if c <= ' ' && isSpace(c) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
if c == '"' {
|
||||
s.step = stateInString
|
||||
return scanBeginLiteral
|
||||
}
|
||||
return s.error(c, "looking for beginning of object key string")
|
||||
}
|
||||
|
||||
// stateEndValue is the state after completing a value,
|
||||
// such as after reading `{}` or `true` or `["x"`.
|
||||
func stateEndValue(s *scanner, c byte) int {
|
||||
n := len(s.parseState)
|
||||
if n == 0 {
|
||||
// Completed top-level before the current byte.
|
||||
s.step = stateEndTop
|
||||
s.endTop = true
|
||||
return stateEndTop(s, c)
|
||||
}
|
||||
if c <= ' ' && isSpace(c) {
|
||||
s.step = stateEndValue
|
||||
return scanSkipSpace
|
||||
}
|
||||
ps := s.parseState[n-1]
|
||||
switch ps {
|
||||
case parseObjectKey:
|
||||
if c == ':' {
|
||||
s.parseState[n-1] = parseObjectValue
|
||||
s.step = stateBeginValue
|
||||
return scanObjectKey
|
||||
}
|
||||
return s.error(c, "after object key")
|
||||
case parseObjectValue:
|
||||
if c == ',' {
|
||||
s.parseState[n-1] = parseObjectKey
|
||||
s.step = stateBeginString
|
||||
return scanObjectValue
|
||||
}
|
||||
if c == '}' {
|
||||
s.popParseState()
|
||||
return scanEndObject
|
||||
}
|
||||
return s.error(c, "after object key:value pair")
|
||||
case parseArrayValue:
|
||||
if c == ',' {
|
||||
s.step = stateBeginValue
|
||||
return scanArrayValue
|
||||
}
|
||||
if c == ']' {
|
||||
s.popParseState()
|
||||
return scanEndArray
|
||||
}
|
||||
return s.error(c, "after array element")
|
||||
}
|
||||
return s.error(c, "")
|
||||
}
|
||||
|
||||
// stateEndTop is the state after finishing the top-level value,
|
||||
// such as after reading `{}` or `[1,2,3]`.
|
||||
// Only space characters should be seen now.
|
||||
func stateEndTop(s *scanner, c byte) int {
|
||||
if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
|
||||
// The former behaviour has been removed. Now, if anything
|
||||
// other than whitespace follows, we assume a new JSON string
|
||||
// might be starting. This allows us to continue obfuscating
|
||||
// further strings in cases where there are multiple JSON
|
||||
// objects enumerated sequentially within the same input.
|
||||
// This is a common case for ElasticSearch response bodies.
|
||||
s.reset()
|
||||
return s.step(s, c)
|
||||
}
|
||||
return scanEnd
|
||||
}
|
||||
|
||||
// stateInString is the state after reading `"`.
|
||||
func stateInString(s *scanner, c byte) int {
|
||||
if c == '"' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
if c == '\\' {
|
||||
s.step = stateInStringEsc
|
||||
return scanContinue
|
||||
}
|
||||
if c < 0x20 {
|
||||
return s.error(c, "in string literal")
|
||||
}
|
||||
return scanContinue
|
||||
}
|
||||
|
||||
// stateInStringEsc is the state after reading `"\` during a quoted string.
|
||||
func stateInStringEsc(s *scanner, c byte) int {
|
||||
switch c {
|
||||
case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
|
||||
s.step = stateInString
|
||||
return scanContinue
|
||||
case 'u':
|
||||
s.step = stateInStringEscU
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in string escape code")
|
||||
}
|
||||
|
||||
// stateInStringEscU is the state after reading `"\u` during a quoted string.
|
||||
func stateInStringEscU(s *scanner, c byte) int {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInStringEscU1
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
|
||||
func stateInStringEscU1(s *scanner, c byte) int {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInStringEscU12
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
|
||||
func stateInStringEscU12(s *scanner, c byte) int {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInStringEscU123
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
|
||||
func stateInStringEscU123(s *scanner, c byte) int {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInString
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateNeg is the state after reading `-` during a number.
|
||||
func stateNeg(s *scanner, c byte) int {
|
||||
if c == '0' {
|
||||
s.step = state0
|
||||
return scanContinue
|
||||
}
|
||||
if '1' <= c && c <= '9' {
|
||||
s.step = state1
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in numeric literal")
|
||||
}
|
||||
|
||||
// state1 is the state after reading a non-zero integer during a number,
|
||||
// such as after reading `1` or `100` but not `0`.
|
||||
func state1(s *scanner, c byte) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = state1
|
||||
return scanContinue
|
||||
}
|
||||
return state0(s, c)
|
||||
}
|
||||
|
||||
// state0 is the state after reading `0` during a number.
|
||||
func state0(s *scanner, c byte) int {
|
||||
if c == '.' {
|
||||
s.step = stateDot
|
||||
return scanContinue
|
||||
}
|
||||
if c == 'e' || c == 'E' {
|
||||
s.step = stateE
|
||||
return scanContinue
|
||||
}
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
|
||||
// stateDot is the state after reading the integer and decimal point in a number,
|
||||
// such as after reading `1.`.
|
||||
func stateDot(s *scanner, c byte) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = stateDot0
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "after decimal point in numeric literal")
|
||||
}
|
||||
|
||||
// stateDot0 is the state after reading the integer, decimal point, and subsequent
|
||||
// digits of a number, such as after reading `3.14`.
|
||||
func stateDot0(s *scanner, c byte) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
return scanContinue
|
||||
}
|
||||
if c == 'e' || c == 'E' {
|
||||
s.step = stateE
|
||||
return scanContinue
|
||||
}
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
|
||||
// stateE is the state after reading the mantissa and e in a number,
|
||||
// such as after reading `314e` or `0.314e`.
|
||||
func stateE(s *scanner, c byte) int {
|
||||
if c == '+' || c == '-' {
|
||||
s.step = stateESign
|
||||
return scanContinue
|
||||
}
|
||||
return stateESign(s, c)
|
||||
}
|
||||
|
||||
// stateESign is the state after reading the mantissa, e, and sign in a number,
|
||||
// such as after reading `314e-` or `0.314e+`.
|
||||
func stateESign(s *scanner, c byte) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = stateE0
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in exponent of numeric literal")
|
||||
}
|
||||
|
||||
// stateE0 is the state after reading the mantissa, e, optional sign,
|
||||
// and at least one digit of the exponent in a number,
|
||||
// such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
|
||||
func stateE0(s *scanner, c byte) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
return scanContinue
|
||||
}
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
|
||||
// stateT is the state after reading `t`.
|
||||
func stateT(s *scanner, c byte) int {
|
||||
if c == 'r' {
|
||||
s.step = stateTr
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal true (expecting 'r')")
|
||||
}
|
||||
|
||||
// stateTr is the state after reading `tr`.
|
||||
func stateTr(s *scanner, c byte) int {
|
||||
if c == 'u' {
|
||||
s.step = stateTru
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal true (expecting 'u')")
|
||||
}
|
||||
|
||||
// stateTru is the state after reading `tru`.
|
||||
func stateTru(s *scanner, c byte) int {
|
||||
if c == 'e' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal true (expecting 'e')")
|
||||
}
|
||||
|
||||
// stateF is the state after reading `f`.
|
||||
func stateF(s *scanner, c byte) int {
|
||||
if c == 'a' {
|
||||
s.step = stateFa
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 'a')")
|
||||
}
|
||||
|
||||
// stateFa is the state after reading `fa`.
|
||||
func stateFa(s *scanner, c byte) int {
|
||||
if c == 'l' {
|
||||
s.step = stateFal
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 'l')")
|
||||
}
|
||||
|
||||
// stateFal is the state after reading `fal`.
|
||||
func stateFal(s *scanner, c byte) int {
|
||||
if c == 's' {
|
||||
s.step = stateFals
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 's')")
|
||||
}
|
||||
|
||||
// stateFals is the state after reading `fals`.
|
||||
func stateFals(s *scanner, c byte) int {
|
||||
if c == 'e' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 'e')")
|
||||
}
|
||||
|
||||
// stateN is the state after reading `n`.
|
||||
func stateN(s *scanner, c byte) int {
|
||||
if c == 'u' {
|
||||
s.step = stateNu
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal null (expecting 'u')")
|
||||
}
|
||||
|
||||
// stateNu is the state after reading `nu`.
|
||||
func stateNu(s *scanner, c byte) int {
|
||||
if c == 'l' {
|
||||
s.step = stateNul
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal null (expecting 'l')")
|
||||
}
|
||||
|
||||
// stateNul is the state after reading `nul`.
|
||||
func stateNul(s *scanner, c byte) int {
|
||||
if c == 'l' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal null (expecting 'l')")
|
||||
}
|
||||
|
||||
// stateError is the state after reaching a syntax error,
|
||||
// such as after reading `[1}` or `5.1.2`.
|
||||
func stateError(s *scanner, c byte) int {
|
||||
return scanError
|
||||
}
|
||||
|
||||
// error records an error and switches to the error state.
|
||||
func (s *scanner) error(c byte, context string) int {
|
||||
s.step = stateError
|
||||
s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes}
|
||||
return scanError
|
||||
}
|
||||
|
||||
// quoteChar formats c as a quoted character literal
|
||||
func quoteChar(c byte) string {
|
||||
// special cases - different from quoted strings
|
||||
if c == '\'' {
|
||||
return `'\''`
|
||||
}
|
||||
if c == '"' {
|
||||
return `'"'`
|
||||
}
|
||||
|
||||
// use quoted string with different quotation marks
|
||||
s := strconv.Quote(string(c))
|
||||
return "'" + s[1:len(s)-1] + "'"
|
||||
}
|
||||
19
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/memcached.go
generated
vendored
Normal file
19
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/memcached.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
package obfuscate
|
||||
|
||||
import "strings"
|
||||
|
||||
// ObfuscateMemcachedString obfuscates the Memcached command cmd.
|
||||
func (*Obfuscator) ObfuscateMemcachedString(cmd string) string {
|
||||
// All memcached commands end with new lines [1]. In the case of storage
|
||||
// commands, key values follow after. Knowing this, all we have to do
|
||||
// to obfuscate sensitive information is to remove everything that follows
|
||||
// a new line. For non-storage commands, this will have no effect.
|
||||
// [1]: https://github.com/memcached/memcached/blob/master/doc/protocol.txt
|
||||
out := strings.SplitN(cmd, "\r\n", 2)[0]
|
||||
return strings.TrimSpace(out)
|
||||
}
|
||||
233
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate.go
generated
vendored
Normal file
233
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate.go
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
// Package obfuscate implements quantizing and obfuscating of tags and resources for
|
||||
// a set of spans matching a certain criteria.
|
||||
//
|
||||
// This module is used in the Datadog Agent, the Go tracing client (dd-trace-go) and in the
|
||||
// OpenTelemetry Collector Datadog exporter./ End-user behavior is stable, but there are no
|
||||
// stability guarantees on its public Go API. Nonetheless, if editing try to avoid breaking
|
||||
// API changes if possible and double check the API usage on all module dependents.
|
||||
package obfuscate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/DataDog/datadog-go/statsd"
|
||||
)
|
||||
|
||||
//go:generate easyjson -no_std_marshalers $GOFILE
|
||||
|
||||
// Obfuscator quantizes and obfuscates spans. The obfuscator is not safe for
|
||||
// concurrent use.
|
||||
type Obfuscator struct {
|
||||
opts *Config
|
||||
es *jsonObfuscator // nil if disabled
|
||||
mongo *jsonObfuscator // nil if disabled
|
||||
sqlExecPlan *jsonObfuscator // nil if disabled
|
||||
sqlExecPlanNormalize *jsonObfuscator // nil if disabled
|
||||
// sqlLiteralEscapes reports whether we should treat escape characters literally or as escape characters.
|
||||
// A non-zero value means 'yes'. Different SQL engines behave in different ways and the tokenizer needs
|
||||
// to be generic.
|
||||
// Not safe for concurrent use.
|
||||
sqlLiteralEscapes int32
|
||||
// queryCache keeps a cache of already obfuscated queries.
|
||||
queryCache *measuredCache
|
||||
log Logger
|
||||
}
|
||||
|
||||
// Logger is able to log certain log messages.
|
||||
type Logger interface {
|
||||
// Debugf logs the given message using the given format.
|
||||
Debugf(format string, params ...interface{})
|
||||
}
|
||||
|
||||
type noopLogger struct{}
|
||||
|
||||
func (noopLogger) Debugf(_ string, _ ...interface{}) {}
|
||||
|
||||
// setSQLLiteralEscapes sets whether or not escape characters should be treated literally by the SQL obfuscator.
|
||||
func (o *Obfuscator) setSQLLiteralEscapes(ok bool) {
|
||||
if ok {
|
||||
atomic.StoreInt32(&o.sqlLiteralEscapes, 1)
|
||||
} else {
|
||||
atomic.StoreInt32(&o.sqlLiteralEscapes, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// useSQLLiteralEscapes reports whether escape characters will be treated literally by the SQL obfuscator.
|
||||
// Some SQL engines require it and others don't. It will be detected as SQL queries are being obfuscated
|
||||
// through calls to ObfuscateSQLString and automatically set for future.
|
||||
func (o *Obfuscator) useSQLLiteralEscapes() bool {
|
||||
return atomic.LoadInt32(&o.sqlLiteralEscapes) == 1
|
||||
}
|
||||
|
||||
// Config holds the configuration for obfuscating sensitive data for various span types.
|
||||
type Config struct {
|
||||
// SQL holds the obfuscation configuration for SQL queries.
|
||||
SQL SQLConfig
|
||||
|
||||
// ES holds the obfuscation configuration for ElasticSearch bodies.
|
||||
ES JSONConfig
|
||||
|
||||
// Mongo holds the obfuscation configuration for MongoDB queries.
|
||||
Mongo JSONConfig
|
||||
|
||||
// SQLExecPlan holds the obfuscation configuration for SQL Exec Plans. This is strictly for safety related obfuscation,
|
||||
// not normalization. Normalization of exec plans is configured in SQLExecPlanNormalize.
|
||||
SQLExecPlan JSONConfig
|
||||
|
||||
// SQLExecPlanNormalize holds the normalization configuration for SQL Exec Plans.
|
||||
SQLExecPlanNormalize JSONConfig
|
||||
|
||||
// HTTP holds the obfuscation settings for HTTP URLs.
|
||||
HTTP HTTPConfig
|
||||
|
||||
// Statsd specifies the statsd client to use for reporting metrics.
|
||||
Statsd StatsClient
|
||||
|
||||
// Logger specifies the logger to use when outputting messages.
|
||||
// If unset, no logs will be outputted.
|
||||
Logger Logger
|
||||
}
|
||||
|
||||
// StatsClient implementations are able to emit stats.
|
||||
type StatsClient interface {
|
||||
// Gauge reports a gauge stat with the given name, value, tags and rate.
|
||||
Gauge(name string, value float64, tags []string, rate float64) error
|
||||
}
|
||||
|
||||
// SQLConfig holds the config for obfuscating SQL.
|
||||
// easyjson:json
|
||||
type SQLConfig struct {
|
||||
// TableNames specifies whether the obfuscator should also extract the table names that a query addresses,
|
||||
// in addition to obfuscating.
|
||||
TableNames bool
|
||||
|
||||
// ReplaceDigits specifies whether digits in table names and identifiers should be obfuscated.
|
||||
ReplaceDigits bool `json:"replace_digits"`
|
||||
|
||||
// KeepSQLAlias reports whether SQL aliases ("AS") should be truncated.
|
||||
KeepSQLAlias bool
|
||||
|
||||
// DollarQuotedFunc reports whether to treat "$func$" delimited dollar-quoted strings
|
||||
// differently and not obfuscate them as a string. To read more about dollar quoted
|
||||
// strings see:
|
||||
//
|
||||
// https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING
|
||||
DollarQuotedFunc bool
|
||||
|
||||
// Cache reports whether the obfuscator should use a LRU look-up cache for SQL obfuscations.
|
||||
Cache bool
|
||||
}
|
||||
|
||||
// HTTPConfig holds the configuration settings for HTTP obfuscation.
|
||||
type HTTPConfig struct {
|
||||
// RemoveQueryStrings determines query strings to be removed from HTTP URLs.
|
||||
RemoveQueryString bool
|
||||
|
||||
// RemovePathDigits determines digits in path segments to be obfuscated.
|
||||
RemovePathDigits bool
|
||||
}
|
||||
|
||||
// JSONConfig holds the obfuscation configuration for sensitive
|
||||
// data found in JSON objects.
|
||||
type JSONConfig struct {
|
||||
// Enabled will specify whether obfuscation should be enabled.
|
||||
Enabled bool
|
||||
|
||||
// KeepValues will specify a set of keys for which their values will
|
||||
// not be obfuscated.
|
||||
KeepValues []string
|
||||
|
||||
// ObfuscateSQLValues will specify a set of keys for which their values
|
||||
// will be passed through SQL obfuscation
|
||||
ObfuscateSQLValues []string
|
||||
}
|
||||
|
||||
// NewObfuscator creates a new obfuscator
|
||||
func NewObfuscator(cfg Config) *Obfuscator {
|
||||
if cfg.Logger == nil {
|
||||
cfg.Logger = noopLogger{}
|
||||
}
|
||||
o := Obfuscator{
|
||||
opts: &cfg,
|
||||
queryCache: newMeasuredCache(cacheOptions{On: cfg.SQL.Cache, Statsd: cfg.Statsd}),
|
||||
}
|
||||
if cfg.ES.Enabled {
|
||||
o.es = newJSONObfuscator(&cfg.ES, &o)
|
||||
}
|
||||
if cfg.Mongo.Enabled {
|
||||
o.mongo = newJSONObfuscator(&cfg.Mongo, &o)
|
||||
}
|
||||
if cfg.SQLExecPlan.Enabled {
|
||||
o.sqlExecPlan = newJSONObfuscator(&cfg.SQLExecPlan, &o)
|
||||
}
|
||||
if cfg.SQLExecPlanNormalize.Enabled {
|
||||
o.sqlExecPlanNormalize = newJSONObfuscator(&cfg.SQLExecPlanNormalize, &o)
|
||||
}
|
||||
if cfg.Statsd == nil {
|
||||
cfg.Statsd = &statsd.NoOpClient{}
|
||||
}
|
||||
return &o
|
||||
}
|
||||
|
||||
// Stop cleans up after a finished Obfuscator.
|
||||
func (o *Obfuscator) Stop() {
|
||||
o.queryCache.Close()
|
||||
}
|
||||
|
||||
// compactWhitespaces compacts all whitespaces in t.
|
||||
func compactWhitespaces(t string) string {
|
||||
n := len(t)
|
||||
r := make([]byte, n)
|
||||
spaceCode := uint8(32)
|
||||
isWhitespace := func(char uint8) bool { return char == spaceCode }
|
||||
nr := 0
|
||||
offset := 0
|
||||
for i := 0; i < n; i++ {
|
||||
if isWhitespace(t[i]) {
|
||||
copy(r[nr:], t[nr+offset:i])
|
||||
r[i-offset] = spaceCode
|
||||
nr = i + 1 - offset
|
||||
for j := i + 1; j < n; j++ {
|
||||
if !isWhitespace(t[j]) {
|
||||
offset += j - i - 1
|
||||
i = j
|
||||
break
|
||||
} else if j == n-1 {
|
||||
offset += j - i
|
||||
i = j
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
copy(r[nr:], t[nr+offset:n])
|
||||
r = r[:n-offset]
|
||||
return string(bytes.Trim(r, " "))
|
||||
}
|
||||
|
||||
// replaceDigits replaces consecutive sequences of digits with '?',
|
||||
// example: "jobs_2020_1597876964" --> "jobs_?_?"
|
||||
func replaceDigits(buffer []byte) []byte {
|
||||
scanningDigit := false
|
||||
filtered := buffer[:0]
|
||||
for _, b := range buffer {
|
||||
// digits are encoded as 1 byte in utf8
|
||||
if isDigit(rune(b)) {
|
||||
if scanningDigit {
|
||||
continue
|
||||
}
|
||||
scanningDigit = true
|
||||
filtered = append(filtered, byte('?'))
|
||||
continue
|
||||
}
|
||||
scanningDigit = false
|
||||
filtered = append(filtered, b)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
100
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate_easyjson.go
generated
vendored
Normal file
100
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/obfuscate_easyjson.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
|
||||
|
||||
package obfuscate
|
||||
|
||||
import (
|
||||
json "encoding/json"
|
||||
|
||||
easyjson "github.com/mailru/easyjson"
|
||||
jlexer "github.com/mailru/easyjson/jlexer"
|
||||
jwriter "github.com/mailru/easyjson/jwriter"
|
||||
)
|
||||
|
||||
// suppress unused package warning
|
||||
var (
|
||||
_ *json.RawMessage
|
||||
_ *jlexer.Lexer
|
||||
_ *jwriter.Writer
|
||||
_ easyjson.Marshaler
|
||||
)
|
||||
|
||||
func easyjson4ef41860DecodeGithubComDataDogDatadogAgentPkgObfuscate(in *jlexer.Lexer, out *SQLConfig) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "TableNames":
|
||||
out.TableNames = bool(in.Bool())
|
||||
case "replace_digits":
|
||||
out.ReplaceDigits = bool(in.Bool())
|
||||
case "KeepSQLAlias":
|
||||
out.KeepSQLAlias = bool(in.Bool())
|
||||
case "DollarQuotedFunc":
|
||||
out.DollarQuotedFunc = bool(in.Bool())
|
||||
case "Cache":
|
||||
out.Cache = bool(in.Bool())
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson4ef41860EncodeGithubComDataDogDatadogAgentPkgObfuscate(out *jwriter.Writer, in SQLConfig) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
{
|
||||
const prefix string = ",\"TableNames\":"
|
||||
out.RawString(prefix[1:])
|
||||
out.Bool(bool(in.TableNames))
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"replace_digits\":"
|
||||
out.RawString(prefix)
|
||||
out.Bool(bool(in.ReplaceDigits))
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"KeepSQLAlias\":"
|
||||
out.RawString(prefix)
|
||||
out.Bool(bool(in.KeepSQLAlias))
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"DollarQuotedFunc\":"
|
||||
out.RawString(prefix)
|
||||
out.Bool(bool(in.DollarQuotedFunc))
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"Cache\":"
|
||||
out.RawString(prefix)
|
||||
out.Bool(bool(in.Cache))
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v SQLConfig) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjson4ef41860EncodeGithubComDataDogDatadogAgentPkgObfuscate(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *SQLConfig) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson4ef41860DecodeGithubComDataDogDatadogAgentPkgObfuscate(l, v)
|
||||
}
|
||||
262
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/redis.go
generated
vendored
Normal file
262
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/redis.go
generated
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
package obfuscate
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// redisTruncationMark is used as suffix by tracing libraries to indicate that a
|
||||
// command was truncated.
|
||||
const redisTruncationMark = "..."
|
||||
|
||||
const maxRedisNbCommands = 3
|
||||
|
||||
// Redis commands consisting in 2 words
|
||||
var redisCompoundCommandSet = map[string]bool{
|
||||
"CLIENT": true, "CLUSTER": true, "COMMAND": true, "CONFIG": true, "DEBUG": true, "SCRIPT": true}
|
||||
|
||||
// QuantizeRedisString returns a quantized version of a Redis query.
|
||||
//
|
||||
// TODO(gbbr): Refactor this method to use the tokenizer and
|
||||
// remove "compactWhitespaces". This method is buggy when commands
|
||||
// contain quoted strings with newlines.
|
||||
func (*Obfuscator) QuantizeRedisString(query string) string {
|
||||
query = compactWhitespaces(query)
|
||||
|
||||
var resource strings.Builder
|
||||
truncated := false
|
||||
nbCmds := 0
|
||||
|
||||
for len(query) > 0 && nbCmds < maxRedisNbCommands {
|
||||
var rawLine string
|
||||
|
||||
// Read the next command
|
||||
idx := strings.IndexByte(query, '\n')
|
||||
if idx == -1 {
|
||||
rawLine = query
|
||||
query = ""
|
||||
} else {
|
||||
rawLine = query[:idx]
|
||||
query = query[idx+1:]
|
||||
}
|
||||
|
||||
line := strings.Trim(rawLine, " ")
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse arguments
|
||||
args := strings.SplitN(line, " ", 3)
|
||||
|
||||
if strings.HasSuffix(args[0], redisTruncationMark) {
|
||||
truncated = true
|
||||
continue
|
||||
}
|
||||
|
||||
command := strings.ToUpper(args[0])
|
||||
|
||||
if redisCompoundCommandSet[command] && len(args) > 1 {
|
||||
if strings.HasSuffix(args[1], redisTruncationMark) {
|
||||
truncated = true
|
||||
continue
|
||||
}
|
||||
|
||||
command += " " + strings.ToUpper(args[1])
|
||||
}
|
||||
|
||||
// Write the command representation
|
||||
resource.WriteByte(' ')
|
||||
resource.WriteString(command)
|
||||
|
||||
nbCmds++
|
||||
truncated = false
|
||||
}
|
||||
|
||||
if nbCmds == maxRedisNbCommands || truncated {
|
||||
resource.WriteString(" ...")
|
||||
}
|
||||
|
||||
return strings.Trim(resource.String(), " ")
|
||||
}
|
||||
|
||||
// ObfuscateRedisString obfuscates the given Redis command.
|
||||
func (*Obfuscator) ObfuscateRedisString(rediscmd string) string {
|
||||
t := newRedisTokenizer([]byte(rediscmd))
|
||||
var (
|
||||
str strings.Builder
|
||||
cmd string
|
||||
args []string
|
||||
)
|
||||
for {
|
||||
tok, typ, done := t.scan()
|
||||
switch typ {
|
||||
case redisTokenCommand:
|
||||
// new command starting
|
||||
if cmd != "" {
|
||||
// a previous command was buffered, obfuscate it
|
||||
obfuscateRedisCmd(&str, cmd, args...)
|
||||
str.WriteByte('\n')
|
||||
}
|
||||
cmd = tok
|
||||
args = args[:0]
|
||||
case redisTokenArgument:
|
||||
args = append(args, tok)
|
||||
}
|
||||
if done {
|
||||
// last command
|
||||
obfuscateRedisCmd(&str, cmd, args...)
|
||||
break
|
||||
}
|
||||
}
|
||||
return str.String()
|
||||
}
|
||||
|
||||
func obfuscateRedisCmd(out *strings.Builder, cmd string, args ...string) {
|
||||
out.WriteString(cmd)
|
||||
if len(args) == 0 {
|
||||
return
|
||||
}
|
||||
out.WriteByte(' ')
|
||||
|
||||
switch strings.ToUpper(cmd) {
|
||||
case "AUTH":
|
||||
// Obfuscate everything after command
|
||||
// • AUTH password
|
||||
if len(args) > 0 {
|
||||
args[0] = "?"
|
||||
args = args[:1]
|
||||
}
|
||||
|
||||
case "APPEND", "GETSET", "LPUSHX", "GEORADIUSBYMEMBER", "RPUSHX",
|
||||
"SET", "SETNX", "SISMEMBER", "ZRANK", "ZREVRANK", "ZSCORE":
|
||||
// Obfuscate 2nd argument:
|
||||
// • APPEND key value
|
||||
// • GETSET key value
|
||||
// • LPUSHX key value
|
||||
// • GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
|
||||
// • RPUSHX key value
|
||||
// • SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
|
||||
// • SETNX key value
|
||||
// • SISMEMBER key member
|
||||
// • ZRANK key member
|
||||
// • ZREVRANK key member
|
||||
// • ZSCORE key member
|
||||
obfuscateRedisArgN(args, 1)
|
||||
|
||||
case "HSET", "HSETNX", "LREM", "LSET", "SETBIT", "SETEX", "PSETEX",
|
||||
"SETRANGE", "ZINCRBY", "SMOVE", "RESTORE":
|
||||
// Obfuscate 3rd argument:
|
||||
// • HSET key field value
|
||||
// • HSETNX key field value
|
||||
// • LREM key count value
|
||||
// • LSET key index value
|
||||
// • SETBIT key offset value
|
||||
// • SETEX key seconds value
|
||||
// • PSETEX key milliseconds value
|
||||
// • SETRANGE key offset value
|
||||
// • ZINCRBY key increment member
|
||||
// • SMOVE source destination member
|
||||
// • RESTORE key ttl serialized-value [REPLACE]
|
||||
obfuscateRedisArgN(args, 2)
|
||||
|
||||
case "LINSERT":
|
||||
// Obfuscate 4th argument:
|
||||
// • LINSERT key BEFORE|AFTER pivot value
|
||||
obfuscateRedisArgN(args, 3)
|
||||
|
||||
case "GEOHASH", "GEOPOS", "GEODIST", "LPUSH", "RPUSH", "SREM",
|
||||
"ZREM", "SADD":
|
||||
// Obfuscate all arguments after the first one.
|
||||
// • GEOHASH key member [member ...]
|
||||
// • GEOPOS key member [member ...]
|
||||
// • GEODIST key member1 member2 [unit]
|
||||
// • LPUSH key value [value ...]
|
||||
// • RPUSH key value [value ...]
|
||||
// • SREM key member [member ...]
|
||||
// • ZREM key member [member ...]
|
||||
// • SADD key member [member ...]
|
||||
if len(args) > 1 {
|
||||
args[1] = "?"
|
||||
args = args[:2]
|
||||
}
|
||||
|
||||
case "GEOADD":
|
||||
// Obfuscating every 3rd argument starting from first
|
||||
// • GEOADD key longitude latitude member [longitude latitude member ...]
|
||||
obfuscateRedisArgsStep(args, 1, 3)
|
||||
|
||||
case "HMSET":
|
||||
// Every 2nd argument starting from first.
|
||||
// • HMSET key field value [field value ...]
|
||||
obfuscateRedisArgsStep(args, 1, 2)
|
||||
|
||||
case "MSET", "MSETNX":
|
||||
// Every 2nd argument starting from command.
|
||||
// • MSET key value [key value ...]
|
||||
// • MSETNX key value [key value ...]
|
||||
obfuscateRedisArgsStep(args, 0, 2)
|
||||
|
||||
case "CONFIG":
|
||||
// Obfuscate 2nd argument to SET sub-command.
|
||||
// • CONFIG SET parameter value
|
||||
if strings.ToUpper(args[0]) == "SET" {
|
||||
obfuscateRedisArgN(args, 2)
|
||||
}
|
||||
|
||||
case "BITFIELD":
|
||||
// Obfuscate 3rd argument to SET sub-command:
|
||||
// • BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
|
||||
var n int
|
||||
for i, arg := range args {
|
||||
if strings.ToUpper(arg) == "SET" {
|
||||
n = i
|
||||
}
|
||||
if n > 0 && i-n == 3 {
|
||||
args[i] = "?"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
case "ZADD":
|
||||
// Obfuscate every 2nd argument after potential optional ones.
|
||||
// • ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
|
||||
var i int
|
||||
loop:
|
||||
for i = range args {
|
||||
if i == 0 {
|
||||
continue // key
|
||||
}
|
||||
switch args[i] {
|
||||
case "NX", "XX", "CH", "INCR":
|
||||
// continue
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
obfuscateRedisArgsStep(args, i, 2)
|
||||
|
||||
default:
|
||||
// Obfuscate nothing.
|
||||
}
|
||||
out.WriteString(strings.Join(args, " "))
|
||||
}
|
||||
|
||||
func obfuscateRedisArgN(args []string, n int) {
|
||||
if len(args) > n {
|
||||
args[n] = "?"
|
||||
}
|
||||
}
|
||||
|
||||
func obfuscateRedisArgsStep(args []string, start, step int) {
|
||||
if start+step-1 >= len(args) {
|
||||
// can't reach target
|
||||
return
|
||||
}
|
||||
for i := start + step - 1; i < len(args); i += step {
|
||||
args[i] = "?"
|
||||
}
|
||||
}
|
||||
187
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/redis_tokenizer.go
generated
vendored
Normal file
187
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/redis_tokenizer.go
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
package obfuscate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// redisTokenType specifies the token type returned by the tokenizer.
|
||||
type redisTokenType int
|
||||
|
||||
const (
|
||||
// redisTokenCommand is a command token. For compound tokens, it is
|
||||
// only the first part up to a space.
|
||||
redisTokenCommand redisTokenType = iota
|
||||
|
||||
// redisTokenArgument is an argument token.
|
||||
redisTokenArgument
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (t redisTokenType) String() string {
|
||||
return map[redisTokenType]string{
|
||||
redisTokenCommand: "command",
|
||||
redisTokenArgument: "argument",
|
||||
}[t]
|
||||
}
|
||||
|
||||
// redisTokenizer tokenizes a Redis command string. The string can be on
|
||||
// multiple lines. The tokenizer is capable of parsing quoted strings and escape
|
||||
// sequences inside them.
|
||||
type redisTokenizer struct {
|
||||
data []byte
|
||||
ch byte
|
||||
off int
|
||||
done bool
|
||||
state redisParseState
|
||||
}
|
||||
|
||||
// redisParseState specifies the current state of the tokenizer.
|
||||
type redisParseState int
|
||||
|
||||
const (
|
||||
// redisStateCommand specifies that we are about to parse a command.
|
||||
// It is usually the state at the beginning of the scan or after a
|
||||
// new line.
|
||||
redisStateCommand redisParseState = iota
|
||||
|
||||
// redisStateArgument specifies that we are about to parse an argument
|
||||
// to a command or the rest of the tokens in a compound command.
|
||||
redisStateArgument
|
||||
)
|
||||
|
||||
// newRedisTokenizer returns a new tokenizer for the given data.
|
||||
func newRedisTokenizer(data []byte) *redisTokenizer {
|
||||
return &redisTokenizer{
|
||||
data: bytes.TrimSpace(data),
|
||||
off: -1,
|
||||
state: redisStateCommand,
|
||||
}
|
||||
}
|
||||
|
||||
// scan returns the next token, it's type and a bool. The boolean specifies if
|
||||
// the returned token was the last one.
|
||||
func (t *redisTokenizer) scan() (tok string, typ redisTokenType, done bool) {
|
||||
switch t.state {
|
||||
case redisStateCommand:
|
||||
return t.scanCommand()
|
||||
default:
|
||||
return t.scanArg()
|
||||
}
|
||||
}
|
||||
|
||||
// next advances the scanner to the next character.
|
||||
func (t *redisTokenizer) next() {
|
||||
t.off++
|
||||
if t.off <= len(t.data)-1 {
|
||||
t.ch = t.data[t.off]
|
||||
return
|
||||
}
|
||||
t.done = true
|
||||
}
|
||||
|
||||
// scanCommand scans a command from the buffer.
|
||||
func (t *redisTokenizer) scanCommand() (tok string, typ redisTokenType, done bool) {
|
||||
var (
|
||||
str strings.Builder
|
||||
started bool
|
||||
)
|
||||
for {
|
||||
t.next()
|
||||
if t.done {
|
||||
return str.String(), typ, t.done
|
||||
}
|
||||
switch t.ch {
|
||||
case ' ':
|
||||
if !started {
|
||||
// skip spaces preceding token
|
||||
t.skipSpace()
|
||||
break
|
||||
}
|
||||
// done scanning command
|
||||
t.state = redisStateArgument
|
||||
t.skipSpace()
|
||||
return str.String(), redisTokenCommand, t.done
|
||||
case '\n':
|
||||
return str.String(), redisTokenCommand, t.done
|
||||
default:
|
||||
str.WriteByte(t.ch)
|
||||
}
|
||||
started = true
|
||||
}
|
||||
}
|
||||
|
||||
// scanArg scans an argument from the buffer.
|
||||
func (t *redisTokenizer) scanArg() (tok string, typ redisTokenType, done bool) {
|
||||
var (
|
||||
str strings.Builder
|
||||
quoted bool // in quoted string
|
||||
escape bool // escape sequence
|
||||
)
|
||||
for {
|
||||
t.next()
|
||||
if t.done {
|
||||
return str.String(), redisTokenArgument, t.done
|
||||
}
|
||||
switch t.ch {
|
||||
case '\\':
|
||||
str.WriteByte('\\')
|
||||
if !escape {
|
||||
// next character could be escaped
|
||||
escape = true
|
||||
continue
|
||||
}
|
||||
case '\n':
|
||||
if !quoted {
|
||||
// last argument, new command follows
|
||||
t.state = redisStateCommand
|
||||
return str.String(), redisTokenArgument, t.done
|
||||
}
|
||||
str.WriteByte('\n')
|
||||
case '"':
|
||||
str.WriteByte('"')
|
||||
if !escape {
|
||||
// this quote wasn't escaped, toggle quoted mode
|
||||
quoted = !quoted
|
||||
}
|
||||
case ' ':
|
||||
if !quoted {
|
||||
t.skipSpace()
|
||||
return str.String(), redisTokenArgument, t.done
|
||||
}
|
||||
str.WriteByte(' ')
|
||||
default:
|
||||
str.WriteByte(t.ch)
|
||||
}
|
||||
escape = false
|
||||
}
|
||||
}
|
||||
|
||||
// unread is the reverse of next, unreading a character.
|
||||
func (t *redisTokenizer) unread() {
|
||||
if t.off < 1 {
|
||||
return
|
||||
}
|
||||
t.off--
|
||||
t.ch = t.data[t.off]
|
||||
}
|
||||
|
||||
// skipSpace moves the cursor forward until it meets the last space
|
||||
// in a sequence of contiguous spaces.
|
||||
func (t *redisTokenizer) skipSpace() {
|
||||
for t.ch == ' ' || t.ch == '\t' || t.ch == '\r' && !t.done {
|
||||
t.next()
|
||||
}
|
||||
if t.ch == '\n' {
|
||||
// next token is a command
|
||||
t.state = redisStateCommand
|
||||
} else {
|
||||
// don't steal the first non-space character
|
||||
t.unread()
|
||||
}
|
||||
}
|
||||
368
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql.go
generated
vendored
Normal file
368
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql.go
generated
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
package obfuscate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var questionMark = []byte("?")
|
||||
|
||||
// discardFilter is a token filter which discards certain elements from a query, such as
|
||||
// comments and AS aliases by returning a nil buffer.
|
||||
type discardFilter struct{ keepSQLAlias bool }
|
||||
|
||||
// Filter the given token so that a `nil` slice is returned if the token is in the token filtered list.
|
||||
func (f *discardFilter) Filter(token, lastToken TokenKind, buffer []byte) (TokenKind, []byte, error) {
|
||||
// filters based on previous token
|
||||
switch lastToken {
|
||||
case FilteredBracketedIdentifier:
|
||||
if token != ']' {
|
||||
// we haven't found the closing bracket yet, keep going
|
||||
if token != ID {
|
||||
// the token between the brackets *must* be an identifier,
|
||||
// otherwise the query is invalid.
|
||||
return LexError, nil, fmt.Errorf("expected identifier in bracketed filter, got %d", token)
|
||||
}
|
||||
return FilteredBracketedIdentifier, nil, nil
|
||||
}
|
||||
fallthrough
|
||||
case As:
|
||||
if token == '[' {
|
||||
// the identifier followed by AS is an MSSQL bracketed identifier
|
||||
// and will continue to be discarded until we find the corresponding
|
||||
// closing bracket counter-part. See GitHub issue DataDog/datadog-trace-agent#475.
|
||||
return FilteredBracketedIdentifier, nil, nil
|
||||
}
|
||||
if f.keepSQLAlias {
|
||||
return token, buffer, nil
|
||||
}
|
||||
return Filtered, nil, nil
|
||||
}
|
||||
|
||||
// filters based on the current token; if the next token should be ignored,
|
||||
// return the same token value (not FilteredGroupable) and nil
|
||||
switch token {
|
||||
case Comment:
|
||||
return Filtered, nil, nil
|
||||
case ';':
|
||||
return markFilteredGroupable(token), nil, nil
|
||||
case As:
|
||||
if !f.keepSQLAlias {
|
||||
return As, nil, nil
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
return token, buffer, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Reset implements tokenFilter.
|
||||
func (f *discardFilter) Reset() {}
|
||||
|
||||
// replaceFilter is a token filter which obfuscates strings and numbers in queries by replacing them
|
||||
// with the "?" character.
|
||||
type replaceFilter struct {
|
||||
replaceDigits bool
|
||||
}
|
||||
|
||||
// Filter the given token so that it will be replaced if in the token replacement list
|
||||
func (f *replaceFilter) Filter(token, lastToken TokenKind, buffer []byte) (tokenType TokenKind, tokenBytes []byte, err error) {
|
||||
switch lastToken {
|
||||
case Savepoint:
|
||||
return markFilteredGroupable(token), questionMark, nil
|
||||
case '=':
|
||||
switch token {
|
||||
case DoubleQuotedString:
|
||||
// double-quoted strings after assignments are eligible for obfuscation
|
||||
return markFilteredGroupable(token), questionMark, nil
|
||||
}
|
||||
}
|
||||
switch token {
|
||||
case DollarQuotedString, String, Number, Null, Variable, PreparedStatement, BooleanLiteral, EscapeSequence:
|
||||
return markFilteredGroupable(token), questionMark, nil
|
||||
case '?':
|
||||
// Cases like 'ARRAY [ ?, ? ]' should be collapsed into 'ARRAY [ ? ]'
|
||||
return markFilteredGroupable(token), questionMark, nil
|
||||
case TableName, ID:
|
||||
if f.replaceDigits {
|
||||
return token, replaceDigits(buffer), nil
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
return token, buffer, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Reset implements tokenFilter.
|
||||
func (f *replaceFilter) Reset() {}
|
||||
|
||||
// groupingFilter is a token filter which groups together items replaced by the replaceFilter. It is meant
|
||||
// to run immediately after it.
|
||||
type groupingFilter struct {
|
||||
groupFilter int // counts the number of values, e.g. 3 = ?, ?, ?
|
||||
groupMulti int // counts the number of groups, e.g. 2 = (?, ?), (?, ?, ?)
|
||||
}
|
||||
|
||||
// Filter the given token so that it will be discarded if a grouping pattern
|
||||
// has been recognized. A grouping is composed by items like:
|
||||
// * '( ?, ?, ? )'
|
||||
// * '( ?, ? ), ( ?, ? )'
|
||||
func (f *groupingFilter) Filter(token, lastToken TokenKind, buffer []byte) (tokenType TokenKind, tokenBytes []byte, err error) {
|
||||
// increasing the number of groups means that we're filtering an entire group
|
||||
// because it can be represented with a single '( ? )'
|
||||
if (lastToken == '(' && isFilteredGroupable(token)) || (token == '(' && f.groupMulti > 0) {
|
||||
f.groupMulti++
|
||||
}
|
||||
|
||||
switch {
|
||||
case f.groupMulti > 0 && lastToken == FilteredGroupableParenthesis && token == ID:
|
||||
// this is the start of a new group that seems to be a nested query;
|
||||
// cancel grouping.
|
||||
f.Reset()
|
||||
return token, append([]byte("( "), buffer...), nil
|
||||
case isFilteredGroupable(token):
|
||||
// the previous filter has dropped this token so we should start
|
||||
// counting the group filter so that we accept only one '?' for
|
||||
// the same group
|
||||
f.groupFilter++
|
||||
|
||||
if f.groupFilter > 1 {
|
||||
return markFilteredGroupable(token), nil, nil
|
||||
}
|
||||
case f.groupFilter > 0 && (token == ',' || token == '?'):
|
||||
// if we are in a group drop all commas
|
||||
return markFilteredGroupable(token), nil, nil
|
||||
case f.groupMulti > 1:
|
||||
// drop all tokens since we're in a counting group
|
||||
// and they're duplicated
|
||||
return markFilteredGroupable(token), nil, nil
|
||||
case token != ',' && token != '(' && token != ')' && !isFilteredGroupable(token):
|
||||
// when we're out of a group reset the filter state
|
||||
f.Reset()
|
||||
}
|
||||
|
||||
return token, buffer, nil
|
||||
}
|
||||
|
||||
// isFilteredGroupable reports whether token is to be considered filtered groupable.
|
||||
func isFilteredGroupable(token TokenKind) bool {
|
||||
switch token {
|
||||
case FilteredGroupable, FilteredGroupableParenthesis:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// markFilteredGroupable returns the appropriate TokenKind to mark this token as
|
||||
// filtered groupable.
|
||||
func markFilteredGroupable(token TokenKind) TokenKind {
|
||||
switch token {
|
||||
case '(':
|
||||
return FilteredGroupableParenthesis
|
||||
default:
|
||||
return FilteredGroupable
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the groupingFilter so that it may be used again.
|
||||
func (f *groupingFilter) Reset() {
|
||||
f.groupFilter = 0
|
||||
f.groupMulti = 0
|
||||
}
|
||||
|
||||
// ObfuscateSQLString quantizes and obfuscates the given input SQL query string. Quantization removes
|
||||
// some elements such as comments and aliases and obfuscation attempts to hide sensitive information
|
||||
// in strings and numbers by redacting them.
|
||||
func (o *Obfuscator) ObfuscateSQLString(in string) (*ObfuscatedQuery, error) {
|
||||
return o.ObfuscateSQLStringWithOptions(in, &o.opts.SQL)
|
||||
}
|
||||
|
||||
// ObfuscateSQLStringWithOptions accepts an optional SQLOptions to change the behavior of the obfuscator
|
||||
// to quantize and obfuscate the given input SQL query string. Quantization removes some elements such as comments
|
||||
// and aliases and obfuscation attempts to hide sensitive information in strings and numbers by redacting them.
|
||||
func (o *Obfuscator) ObfuscateSQLStringWithOptions(in string, opts *SQLConfig) (*ObfuscatedQuery, error) {
|
||||
if v, ok := o.queryCache.Get(in); ok {
|
||||
return v.(*ObfuscatedQuery), nil
|
||||
}
|
||||
oq, err := o.obfuscateSQLString(in, opts)
|
||||
if err != nil {
|
||||
return oq, err
|
||||
}
|
||||
o.queryCache.Set(in, oq, oq.Cost())
|
||||
return oq, nil
|
||||
}
|
||||
|
||||
func (o *Obfuscator) obfuscateSQLString(in string, opts *SQLConfig) (*ObfuscatedQuery, error) {
|
||||
lesc := o.useSQLLiteralEscapes()
|
||||
tok := NewSQLTokenizer(in, lesc, opts)
|
||||
out, err := attemptObfuscation(tok)
|
||||
if err != nil && tok.SeenEscape() {
|
||||
// If the tokenizer failed, but saw an escape character in the process,
|
||||
// try again treating escapes differently
|
||||
tok = NewSQLTokenizer(in, !lesc, opts)
|
||||
if out, err2 := attemptObfuscation(tok); err2 == nil {
|
||||
// If the second attempt succeeded, change the default behavior so that
|
||||
// on the next run we get it right in the first run.
|
||||
o.setSQLLiteralEscapes(!lesc)
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
// tableFinderFilter is a filter which attempts to identify the table name as it goes through each
|
||||
// token in a query.
|
||||
type tableFinderFilter struct {
|
||||
storeTableNames bool
|
||||
// seen keeps track of unique table names encountered by the filter.
|
||||
seen map[string]struct{}
|
||||
// csv specifies a comma-separated list of tables
|
||||
csv strings.Builder
|
||||
}
|
||||
|
||||
// Filter implements tokenFilter.
|
||||
func (f *tableFinderFilter) Filter(token, lastToken TokenKind, buffer []byte) (TokenKind, []byte, error) {
|
||||
switch lastToken {
|
||||
case From, Join:
|
||||
// SELECT ... FROM [tableName]
|
||||
// DELETE FROM [tableName]
|
||||
// ... JOIN [tableName]
|
||||
if r, _ := utf8.DecodeRune(buffer); !unicode.IsLetter(r) {
|
||||
// first character in buffer is not a letter; we might have a nested
|
||||
// query like SELECT * FROM (SELECT ...)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case Update, Into:
|
||||
// UPDATE [tableName]
|
||||
// INSERT INTO [tableName]
|
||||
if f.storeTableNames {
|
||||
f.storeName(string(buffer))
|
||||
}
|
||||
return TableName, buffer, nil
|
||||
}
|
||||
return token, buffer, nil
|
||||
}
|
||||
|
||||
// storeName marks the given table name as seen in the internal storage.
|
||||
func (f *tableFinderFilter) storeName(name string) {
|
||||
if _, ok := f.seen[name]; ok {
|
||||
return
|
||||
}
|
||||
if f.seen == nil {
|
||||
f.seen = make(map[string]struct{}, 1)
|
||||
}
|
||||
f.seen[name] = struct{}{}
|
||||
if f.csv.Len() > 0 {
|
||||
f.csv.WriteByte(',')
|
||||
}
|
||||
f.csv.WriteString(name)
|
||||
}
|
||||
|
||||
// CSV returns a comma-separated list of the tables seen by the filter.
|
||||
func (f *tableFinderFilter) CSV() string { return f.csv.String() }
|
||||
|
||||
// Reset implements tokenFilter.
|
||||
func (f *tableFinderFilter) Reset() {
|
||||
for k := range f.seen {
|
||||
delete(f.seen, k)
|
||||
}
|
||||
f.csv.Reset()
|
||||
}
|
||||
|
||||
// ObfuscatedQuery specifies information about an obfuscated SQL query.
|
||||
type ObfuscatedQuery struct {
|
||||
Query string // the obfuscated SQL query
|
||||
TablesCSV string // comma-separated list of tables that the query addresses
|
||||
}
|
||||
|
||||
// Cost returns the number of bytes needed to store all the fields
|
||||
// of this ObfuscatedQuery.
|
||||
func (oq *ObfuscatedQuery) Cost() int64 {
|
||||
return int64(len(oq.Query) + len(oq.TablesCSV))
|
||||
}
|
||||
|
||||
// attemptObfuscation attempts to obfuscate the SQL query loaded into the tokenizer, using the given set of filters.
|
||||
func attemptObfuscation(tokenizer *SQLTokenizer) (*ObfuscatedQuery, error) {
|
||||
var (
|
||||
storeTableNames = tokenizer.cfg.TableNames
|
||||
out = bytes.NewBuffer(make([]byte, 0, len(tokenizer.buf)))
|
||||
err error
|
||||
lastToken TokenKind
|
||||
discard = discardFilter{tokenizer.cfg.KeepSQLAlias}
|
||||
replace = replaceFilter{replaceDigits: tokenizer.cfg.ReplaceDigits}
|
||||
grouping groupingFilter
|
||||
tableFinder = tableFinderFilter{storeTableNames: storeTableNames}
|
||||
)
|
||||
// call Scan() function until tokens are available or if a LEX_ERROR is raised. After
|
||||
// retrieving a token, send it to the tokenFilter chains so that the token is discarded
|
||||
// or replaced.
|
||||
for {
|
||||
token, buff := tokenizer.Scan()
|
||||
if token == EndChar {
|
||||
break
|
||||
}
|
||||
if token == LexError {
|
||||
return nil, fmt.Errorf("%v", tokenizer.Err())
|
||||
}
|
||||
|
||||
if token, buff, err = discard.Filter(token, lastToken, buff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if storeTableNames {
|
||||
if token, buff, err = tableFinder.Filter(token, lastToken, buff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if token, buff, err = replace.Filter(token, lastToken, buff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token, buff, err = grouping.Filter(token, lastToken, buff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buff != nil {
|
||||
if out.Len() != 0 {
|
||||
switch token {
|
||||
case ',':
|
||||
case '=':
|
||||
if lastToken == ':' {
|
||||
// do not add a space before an equals if a colon was
|
||||
// present before it.
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
out.WriteRune(' ')
|
||||
}
|
||||
}
|
||||
out.Write(buff)
|
||||
}
|
||||
lastToken = token
|
||||
}
|
||||
if out.Len() == 0 {
|
||||
return nil, errors.New("result is empty")
|
||||
}
|
||||
return &ObfuscatedQuery{
|
||||
Query: out.String(),
|
||||
TablesCSV: tableFinder.CSV(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ObfuscateSQLExecPlan obfuscates query conditions in the provided JSON encoded execution plan. If normalize=True,
|
||||
// then cost and row estimates are also obfuscated away.
|
||||
func (o *Obfuscator) ObfuscateSQLExecPlan(jsonPlan string, normalize bool) (string, error) {
|
||||
if normalize {
|
||||
return o.sqlExecPlanNormalize.obfuscate([]byte(jsonPlan))
|
||||
}
|
||||
return o.sqlExecPlan.obfuscate([]byte(jsonPlan))
|
||||
}
|
||||
767
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql_tokenizer.go
generated
vendored
Normal file
767
vendor/github.com/DataDog/datadog-agent/pkg/obfuscate/sql_tokenizer.go
generated
vendored
Normal file
@@ -0,0 +1,767 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
|
||||
package obfuscate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// tokenizer.go implemenents a lexer-like iterator that tokenizes SQL and CQL
|
||||
// strings, so that an external component can filter or alter each token of the
|
||||
// string. This implementation can't be used as a real SQL lexer (so a parser
|
||||
// cannot build the AST) because many rules are ignored to make the tokenizer
|
||||
// simpler.
|
||||
// This implementation was inspired by https://github.com/youtube/vitess sql parser
|
||||
// TODO: add the license to the NOTICE file
|
||||
|
||||
// TokenKind specifies the type of the token being scanned. It may be one of the defined
|
||||
// constants below or in some cases the actual rune itself.
|
||||
type TokenKind uint32
|
||||
|
||||
// EndChar is used to signal that the scanner has finished reading the query. This happens when
|
||||
// there are no more characters left in the query or when invalid encoding is discovered. EndChar
|
||||
// is an invalid rune value that can not be found in any valid string.
|
||||
const EndChar = unicode.MaxRune + 1
|
||||
|
||||
// list of available tokens; this list has been reduced because we don't
|
||||
// need a full-fledged tokenizer to implement a Lexer
|
||||
const (
|
||||
LexError = TokenKind(57346) + iota
|
||||
|
||||
ID
|
||||
Limit
|
||||
Null
|
||||
String
|
||||
DoubleQuotedString
|
||||
DollarQuotedString // https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING
|
||||
DollarQuotedFunc // a dollar-quoted string delimited by the tag "$func$"; gets special treatment when feature "dollar_quoted_func" is set
|
||||
Number
|
||||
BooleanLiteral
|
||||
ValueArg
|
||||
ListArg
|
||||
Comment
|
||||
Variable
|
||||
Savepoint
|
||||
PreparedStatement
|
||||
EscapeSequence
|
||||
NullSafeEqual
|
||||
LE
|
||||
GE
|
||||
NE
|
||||
Not
|
||||
As
|
||||
From
|
||||
Update
|
||||
Insert
|
||||
Into
|
||||
Join
|
||||
TableName
|
||||
ColonCast
|
||||
|
||||
// FilteredGroupable specifies that the given token has been discarded by one of the
|
||||
// token filters and that it is groupable together with consecutive FilteredGroupable
|
||||
// tokens.
|
||||
FilteredGroupable
|
||||
|
||||
// FilteredGroupableParenthesis is a parenthesis marked as filtered groupable. It is the
|
||||
// beginning of either a group of values ('(') or a nested query. We track is as
|
||||
// a special case for when it may start a nested query as opposed to just another
|
||||
// value group to be obfuscated.
|
||||
FilteredGroupableParenthesis
|
||||
|
||||
// Filtered specifies that the token is a comma and was discarded by one
|
||||
// of the filters.
|
||||
Filtered
|
||||
|
||||
// FilteredBracketedIdentifier specifies that we are currently discarding
|
||||
// a bracketed identifier (MSSQL).
|
||||
// See issue https://github.com/DataDog/datadog-trace-agent/issues/475.
|
||||
FilteredBracketedIdentifier
|
||||
)
|
||||
|
||||
var tokenKindStrings = map[TokenKind]string{
|
||||
LexError: "LexError",
|
||||
ID: "ID",
|
||||
Limit: "Limit",
|
||||
Null: "Null",
|
||||
String: "String",
|
||||
DoubleQuotedString: "DoubleQuotedString",
|
||||
DollarQuotedString: "DollarQuotedString",
|
||||
DollarQuotedFunc: "DollarQuotedFunc",
|
||||
Number: "Number",
|
||||
BooleanLiteral: "BooleanLiteral",
|
||||
ValueArg: "ValueArg",
|
||||
ListArg: "ListArg",
|
||||
Comment: "Comment",
|
||||
Variable: "Variable",
|
||||
Savepoint: "Savepoint",
|
||||
PreparedStatement: "PreparedStatement",
|
||||
EscapeSequence: "EscapeSequence",
|
||||
NullSafeEqual: "NullSafeEqual",
|
||||
LE: "LE",
|
||||
GE: "GE",
|
||||
NE: "NE",
|
||||
Not: "NOT",
|
||||
As: "As",
|
||||
From: "From",
|
||||
Update: "Update",
|
||||
Insert: "Insert",
|
||||
Into: "Into",
|
||||
Join: "Join",
|
||||
TableName: "TableName",
|
||||
ColonCast: "ColonCast",
|
||||
FilteredGroupable: "FilteredGroupable",
|
||||
FilteredGroupableParenthesis: "FilteredGroupableParenthesis",
|
||||
Filtered: "Filtered",
|
||||
FilteredBracketedIdentifier: "FilteredBracketedIdentifier",
|
||||
}
|
||||
|
||||
func (k TokenKind) String() string {
|
||||
str, ok := tokenKindStrings[k]
|
||||
if !ok {
|
||||
return "<unknown>"
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
const escapeCharacter = '\\'
|
||||
|
||||
// SQLTokenizer is the struct used to generate SQL
|
||||
// tokens for the parser.
|
||||
type SQLTokenizer struct {
|
||||
pos int // byte offset of lastChar
|
||||
lastChar rune // last read rune
|
||||
buf []byte // buf holds the query that we are parsing
|
||||
off int // off is the index into buf where the unread portion of the query begins.
|
||||
err error // any error occurred while reading
|
||||
|
||||
curlys uint32 // number of active open curly braces in top-level SQL escape sequences.
|
||||
|
||||
literalEscapes bool // indicates we should not treat backslashes as escape characters
|
||||
seenEscape bool // indicates whether this tokenizer has seen an escape character within a string
|
||||
|
||||
cfg *SQLConfig
|
||||
}
|
||||
|
||||
// NewSQLTokenizer creates a new SQLTokenizer for the given SQL string. The literalEscapes argument specifies
|
||||
// whether escape characters should be treated literally or as such.
|
||||
func NewSQLTokenizer(sql string, literalEscapes bool, cfg *SQLConfig) *SQLTokenizer {
|
||||
if cfg == nil {
|
||||
cfg = new(SQLConfig)
|
||||
}
|
||||
return &SQLTokenizer{
|
||||
buf: []byte(sql),
|
||||
cfg: cfg,
|
||||
literalEscapes: literalEscapes,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the underlying buffer and positions
|
||||
func (tkn *SQLTokenizer) Reset(in string) {
|
||||
tkn.pos = 0
|
||||
tkn.lastChar = 0
|
||||
tkn.buf = []byte(in)
|
||||
tkn.off = 0
|
||||
tkn.err = nil
|
||||
}
|
||||
|
||||
// keywords used to recognize string tokens
|
||||
var keywords = map[string]TokenKind{
|
||||
"NULL": Null,
|
||||
"TRUE": BooleanLiteral,
|
||||
"FALSE": BooleanLiteral,
|
||||
"SAVEPOINT": Savepoint,
|
||||
"LIMIT": Limit,
|
||||
"AS": As,
|
||||
"FROM": From,
|
||||
"UPDATE": Update,
|
||||
"INSERT": Insert,
|
||||
"INTO": Into,
|
||||
"JOIN": Join,
|
||||
}
|
||||
|
||||
// Err returns the last error that the tokenizer encountered, or nil.
|
||||
func (tkn *SQLTokenizer) Err() error { return tkn.err }
|
||||
|
||||
func (tkn *SQLTokenizer) setErr(format string, args ...interface{}) {
|
||||
if tkn.err != nil {
|
||||
return
|
||||
}
|
||||
tkn.err = fmt.Errorf("at position %d: %v", tkn.pos, fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// SeenEscape returns whether or not this tokenizer has seen an escape character within a scanned string
|
||||
func (tkn *SQLTokenizer) SeenEscape() bool { return tkn.seenEscape }
|
||||
|
||||
// Scan scans the tokenizer for the next token and returns
|
||||
// the token type and the token buffer.
|
||||
func (tkn *SQLTokenizer) Scan() (TokenKind, []byte) {
|
||||
if tkn.lastChar == 0 {
|
||||
tkn.advance()
|
||||
}
|
||||
tkn.skipBlank()
|
||||
|
||||
switch ch := tkn.lastChar; {
|
||||
case isLeadingLetter(ch):
|
||||
return tkn.scanIdentifier()
|
||||
case isDigit(ch):
|
||||
return tkn.scanNumber(false)
|
||||
default:
|
||||
tkn.advance()
|
||||
if tkn.lastChar == EndChar && tkn.err != nil {
|
||||
// advance discovered an invalid encoding. We should return early.
|
||||
return LexError, nil
|
||||
}
|
||||
switch ch {
|
||||
case EndChar:
|
||||
if tkn.err != nil {
|
||||
return LexError, nil
|
||||
}
|
||||
return EndChar, nil
|
||||
case ':':
|
||||
if tkn.lastChar == ':' {
|
||||
tkn.advance()
|
||||
return ColonCast, []byte("::")
|
||||
}
|
||||
if unicode.IsSpace(tkn.lastChar) {
|
||||
// example scenario: "autovacuum: VACUUM ANALYZE fake.table"
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
}
|
||||
if tkn.lastChar != '=' {
|
||||
return tkn.scanBindVar()
|
||||
}
|
||||
fallthrough
|
||||
case '~':
|
||||
switch tkn.lastChar {
|
||||
case '*':
|
||||
tkn.advance()
|
||||
return TokenKind('~'), []byte("~*")
|
||||
default:
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
}
|
||||
case '=', ',', ';', '(', ')', '+', '*', '&', '|', '^', '[', ']', '?':
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
case '.':
|
||||
if isDigit(tkn.lastChar) {
|
||||
return tkn.scanNumber(true)
|
||||
}
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
case '/':
|
||||
switch tkn.lastChar {
|
||||
case '/':
|
||||
tkn.advance()
|
||||
return tkn.scanCommentType1("//")
|
||||
case '*':
|
||||
tkn.advance()
|
||||
return tkn.scanCommentType2()
|
||||
default:
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
}
|
||||
case '-':
|
||||
switch {
|
||||
case tkn.lastChar == '-':
|
||||
tkn.advance()
|
||||
return tkn.scanCommentType1("--")
|
||||
case isDigit(tkn.lastChar):
|
||||
tkn.advance()
|
||||
kind, tokenBytes := tkn.scanNumber(false)
|
||||
return kind, append([]byte{'-'}, tokenBytes...)
|
||||
default:
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
}
|
||||
case '#':
|
||||
tkn.advance()
|
||||
return tkn.scanCommentType1("#")
|
||||
case '<':
|
||||
switch tkn.lastChar {
|
||||
case '>':
|
||||
tkn.advance()
|
||||
return NE, []byte("<>")
|
||||
case '=':
|
||||
tkn.advance()
|
||||
switch tkn.lastChar {
|
||||
case '>':
|
||||
tkn.advance()
|
||||
return NullSafeEqual, []byte("<=>")
|
||||
default:
|
||||
return LE, []byte("<=")
|
||||
}
|
||||
default:
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
}
|
||||
case '>':
|
||||
if tkn.lastChar == '=' {
|
||||
tkn.advance()
|
||||
return GE, []byte(">=")
|
||||
}
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
case '!':
|
||||
switch tkn.lastChar {
|
||||
case '=':
|
||||
tkn.advance()
|
||||
return NE, []byte("!=")
|
||||
case '~':
|
||||
tkn.advance()
|
||||
switch tkn.lastChar {
|
||||
case '*':
|
||||
tkn.advance()
|
||||
return NE, []byte("!~*")
|
||||
default:
|
||||
return NE, []byte("!~")
|
||||
}
|
||||
default:
|
||||
if isValidCharAfterOperator(tkn.lastChar) {
|
||||
return Not, tkn.bytes()
|
||||
}
|
||||
tkn.setErr(`unexpected char "%c" (%d) after "!"`, tkn.lastChar, tkn.lastChar)
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
case '\'':
|
||||
return tkn.scanString(ch, String)
|
||||
case '"':
|
||||
return tkn.scanString(ch, DoubleQuotedString)
|
||||
case '`':
|
||||
return tkn.scanString(ch, ID)
|
||||
case '%':
|
||||
if tkn.lastChar == '(' {
|
||||
return tkn.scanVariableIdentifier('%')
|
||||
}
|
||||
if isLetter(tkn.lastChar) {
|
||||
// format parameter (e.g. '%s')
|
||||
return tkn.scanFormatParameter('%')
|
||||
}
|
||||
// modulo operator (e.g. 'id % 8')
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
case '$':
|
||||
if isDigit(tkn.lastChar) {
|
||||
// TODO(gbbr): the first digit after $ does not necessarily guarantee
|
||||
// that this isn't a dollar-quoted string constant. We might eventually
|
||||
// want to cover for this use-case too (e.g. $1$some text$1$).
|
||||
return tkn.scanPreparedStatement('$')
|
||||
}
|
||||
kind, tok := tkn.scanDollarQuotedString()
|
||||
if kind == DollarQuotedFunc {
|
||||
// this is considered an embedded query, we should try and
|
||||
// obfuscate it
|
||||
out, err := attemptObfuscation(NewSQLTokenizer(string(tok), tkn.literalEscapes, tkn.cfg))
|
||||
if err != nil {
|
||||
// if we can't obfuscate it, treat it as a regular string
|
||||
return DollarQuotedString, tok
|
||||
}
|
||||
tok = append(append([]byte("$func$"), []byte(out.Query)...), []byte("$func$")...)
|
||||
}
|
||||
return kind, tok
|
||||
case '{':
|
||||
if tkn.pos == 1 || tkn.curlys > 0 {
|
||||
// Do not fully obfuscate top-level SQL escape sequences like {{[?=]call procedure-name[([parameter][,parameter]...)]}.
|
||||
// We want these to display a bit more context than just a plain '?'
|
||||
// See: https://docs.oracle.com/cd/E13157_01/wlevs/docs30/jdbc_drivers/sqlescape.html
|
||||
tkn.curlys++
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
}
|
||||
return tkn.scanEscapeSequence('{')
|
||||
case '}':
|
||||
if tkn.curlys == 0 {
|
||||
// A closing curly brace has no place outside an in-progress top-level SQL escape sequence
|
||||
// started by the '{' switch-case.
|
||||
tkn.setErr(`unexpected byte %d`, ch)
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
tkn.curlys--
|
||||
return TokenKind(ch), tkn.bytes()
|
||||
default:
|
||||
tkn.setErr(`unexpected byte %d`, ch)
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) skipBlank() {
|
||||
for unicode.IsSpace(tkn.lastChar) {
|
||||
tkn.advance()
|
||||
}
|
||||
tkn.bytes()
|
||||
}
|
||||
|
||||
// toUpper is a modified version of bytes.ToUpper. It returns an upper-cased version of the byte
|
||||
// slice src with all Unicode letters mapped to their upper case. It is modified to also accept a
|
||||
// byte slice dst as an argument, the underlying storage of which (up to the capacity of dst)
|
||||
// will be used as the destination of the upper-case copy of src, if it fits. As a special case,
|
||||
// toUpper will return src if the byte slice is already upper-case. This function is used rather
|
||||
// than bytes.ToUpper to improve the memory performance of the obfuscator by saving unnecessary
|
||||
// allocations happening in bytes.ToUpper
|
||||
func toUpper(src, dst []byte) []byte {
|
||||
dst = dst[:0]
|
||||
isASCII, hasLower := true, false
|
||||
for i := 0; i < len(src); i++ {
|
||||
c := src[i]
|
||||
if c >= utf8.RuneSelf {
|
||||
isASCII = false
|
||||
break
|
||||
}
|
||||
hasLower = hasLower || ('a' <= c && c <= 'z')
|
||||
}
|
||||
if cap(dst) < len(src) {
|
||||
dst = make([]byte, 0, len(src))
|
||||
}
|
||||
if isASCII { // optimize for ASCII-only byte slices.
|
||||
if !hasLower {
|
||||
// Just return src.
|
||||
return src
|
||||
}
|
||||
dst = dst[:len(src)]
|
||||
for i := 0; i < len(src); i++ {
|
||||
c := src[i]
|
||||
if 'a' <= c && c <= 'z' {
|
||||
c -= 'a' - 'A'
|
||||
}
|
||||
dst[i] = c
|
||||
}
|
||||
return dst
|
||||
}
|
||||
// This *could* be optimized, but it's an uncommon case.
|
||||
return bytes.Map(unicode.ToUpper, src)
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanIdentifier() (TokenKind, []byte) {
|
||||
tkn.advance()
|
||||
for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || tkn.lastChar == '.' || tkn.lastChar == '*' {
|
||||
tkn.advance()
|
||||
}
|
||||
|
||||
t := tkn.bytes()
|
||||
// Space allows us to upper-case identifiers 256 bytes long or less without allocating heap
|
||||
// storage for them, since space is allocated on the stack. A size of 256 bytes was chosen
|
||||
// based on the allowed length of sql identifiers in various sql implementations.
|
||||
var space [256]byte
|
||||
upper := toUpper(t, space[:0])
|
||||
if keywordID, found := keywords[string(upper)]; found {
|
||||
return keywordID, t
|
||||
}
|
||||
return ID, t
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanVariableIdentifier(prefix rune) (TokenKind, []byte) {
|
||||
for tkn.advance(); tkn.lastChar != ')' && tkn.lastChar != EndChar; tkn.advance() {
|
||||
}
|
||||
tkn.advance()
|
||||
if !isLetter(tkn.lastChar) {
|
||||
tkn.setErr(`invalid character after variable identifier: "%c" (%d)`, tkn.lastChar, tkn.lastChar)
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
tkn.advance()
|
||||
return Variable, tkn.bytes()
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanFormatParameter(prefix rune) (TokenKind, []byte) {
|
||||
tkn.advance()
|
||||
return Variable, tkn.bytes()
|
||||
}
|
||||
|
||||
// scanDollarQuotedString scans a Postgres dollar-quoted string constant.
|
||||
// See: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING
|
||||
func (tkn *SQLTokenizer) scanDollarQuotedString() (TokenKind, []byte) {
|
||||
kind, tag := tkn.scanString('$', String)
|
||||
if kind == LexError {
|
||||
return kind, tkn.bytes()
|
||||
}
|
||||
var (
|
||||
got int
|
||||
buf bytes.Buffer
|
||||
)
|
||||
delim := tag
|
||||
// on empty strings, tkn.scanString returns the delimiters
|
||||
if string(delim) != "$$" {
|
||||
// on non-empty strings, the delimiter is $tag$
|
||||
delim = append([]byte{'$'}, delim...)
|
||||
delim = append(delim, '$')
|
||||
}
|
||||
for {
|
||||
ch := tkn.lastChar
|
||||
tkn.advance()
|
||||
if ch == EndChar {
|
||||
tkn.setErr("unexpected EOF in dollar-quoted string")
|
||||
return LexError, buf.Bytes()
|
||||
}
|
||||
if byte(ch) == delim[got] {
|
||||
got++
|
||||
if got == len(delim) {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if got > 0 {
|
||||
_, err := buf.Write(delim[:got])
|
||||
if err != nil {
|
||||
tkn.setErr("error reading dollar-quoted string: %v", err)
|
||||
return LexError, buf.Bytes()
|
||||
}
|
||||
got = 0
|
||||
}
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
if tkn.cfg.DollarQuotedFunc && string(delim) == "$func$" {
|
||||
return DollarQuotedFunc, buf.Bytes()
|
||||
}
|
||||
return DollarQuotedString, buf.Bytes()
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanPreparedStatement(prefix rune) (TokenKind, []byte) {
|
||||
// a prepared statement expect a digit identifier like $1
|
||||
if !isDigit(tkn.lastChar) {
|
||||
tkn.setErr(`prepared statements must start with digits, got "%c" (%d)`, tkn.lastChar, tkn.lastChar)
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
|
||||
// scanNumber keeps the prefix rune intact.
|
||||
// read numbers and return an error if any
|
||||
token, buff := tkn.scanNumber(false)
|
||||
if token == LexError {
|
||||
tkn.setErr("invalid number")
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
return PreparedStatement, buff
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanEscapeSequence(braces rune) (TokenKind, []byte) {
|
||||
for tkn.lastChar != '}' && tkn.lastChar != EndChar {
|
||||
tkn.advance()
|
||||
}
|
||||
|
||||
// we've reached the end of the string without finding
|
||||
// the closing curly braces
|
||||
if tkn.lastChar == EndChar {
|
||||
tkn.setErr("unexpected EOF in escape sequence")
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
|
||||
tkn.advance()
|
||||
return EscapeSequence, tkn.bytes()
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanBindVar() (TokenKind, []byte) {
|
||||
token := ValueArg
|
||||
if tkn.lastChar == ':' {
|
||||
token = ListArg
|
||||
tkn.advance()
|
||||
}
|
||||
if !isLetter(tkn.lastChar) {
|
||||
tkn.setErr(`bind variables should start with letters, got "%c" (%d)`, tkn.lastChar, tkn.lastChar)
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
for isLetter(tkn.lastChar) || isDigit(tkn.lastChar) || tkn.lastChar == '.' {
|
||||
tkn.advance()
|
||||
}
|
||||
return token, tkn.bytes()
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanMantissa(base int) {
|
||||
for digitVal(tkn.lastChar) < base {
|
||||
tkn.advance()
|
||||
}
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanNumber(seenDecimalPoint bool) (TokenKind, []byte) {
|
||||
if seenDecimalPoint {
|
||||
tkn.scanMantissa(10)
|
||||
goto exponent
|
||||
}
|
||||
|
||||
if tkn.lastChar == '0' {
|
||||
// int or float
|
||||
tkn.advance()
|
||||
if tkn.lastChar == 'x' || tkn.lastChar == 'X' {
|
||||
// hexadecimal int
|
||||
tkn.advance()
|
||||
tkn.scanMantissa(16)
|
||||
} else {
|
||||
// octal int or float
|
||||
seenDecimalDigit := false
|
||||
tkn.scanMantissa(8)
|
||||
if tkn.lastChar == '8' || tkn.lastChar == '9' {
|
||||
// illegal octal int or float
|
||||
seenDecimalDigit = true
|
||||
tkn.scanMantissa(10)
|
||||
}
|
||||
if tkn.lastChar == '.' || tkn.lastChar == 'e' || tkn.lastChar == 'E' {
|
||||
goto fraction
|
||||
}
|
||||
// octal int
|
||||
if seenDecimalDigit {
|
||||
// tkn.setErr called in caller
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
}
|
||||
goto exit
|
||||
}
|
||||
|
||||
// decimal int or float
|
||||
tkn.scanMantissa(10)
|
||||
|
||||
fraction:
|
||||
if tkn.lastChar == '.' {
|
||||
tkn.advance()
|
||||
tkn.scanMantissa(10)
|
||||
}
|
||||
|
||||
exponent:
|
||||
if tkn.lastChar == 'e' || tkn.lastChar == 'E' {
|
||||
tkn.advance()
|
||||
if tkn.lastChar == '+' || tkn.lastChar == '-' {
|
||||
tkn.advance()
|
||||
}
|
||||
tkn.scanMantissa(10)
|
||||
}
|
||||
|
||||
exit:
|
||||
t := tkn.bytes()
|
||||
if len(t) == 0 {
|
||||
return LexError, nil
|
||||
}
|
||||
return Number, t
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanString(delim rune, kind TokenKind) (TokenKind, []byte) {
|
||||
buf := bytes.NewBuffer(tkn.buf[:0])
|
||||
for {
|
||||
ch := tkn.lastChar
|
||||
tkn.advance()
|
||||
if ch == delim {
|
||||
if tkn.lastChar == delim {
|
||||
// doubling a delimiter is the default way to embed the delimiter within a string
|
||||
tkn.advance()
|
||||
} else {
|
||||
// a single delimiter denotes the end of the string
|
||||
break
|
||||
}
|
||||
} else if ch == escapeCharacter {
|
||||
tkn.seenEscape = true
|
||||
|
||||
if !tkn.literalEscapes {
|
||||
// treat as an escape character
|
||||
ch = tkn.lastChar
|
||||
tkn.advance()
|
||||
}
|
||||
}
|
||||
if ch == EndChar {
|
||||
tkn.setErr("unexpected EOF in string")
|
||||
return LexError, buf.Bytes()
|
||||
}
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
if kind == ID && buf.Len() == 0 || bytes.IndexFunc(buf.Bytes(), func(r rune) bool { return !unicode.IsSpace(r) }) == -1 {
|
||||
// This string is an empty or white-space only identifier.
|
||||
// We should keep the start and end delimiters in order to
|
||||
// avoid creating invalid queries.
|
||||
// See: https://github.com/DataDog/datadog-trace-agent/issues/316
|
||||
return kind, append(runeBytes(delim), runeBytes(delim)...)
|
||||
}
|
||||
return kind, buf.Bytes()
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanCommentType1(prefix string) (TokenKind, []byte) {
|
||||
for tkn.lastChar != EndChar {
|
||||
if tkn.lastChar == '\n' {
|
||||
tkn.advance()
|
||||
break
|
||||
}
|
||||
tkn.advance()
|
||||
}
|
||||
return Comment, tkn.bytes()
|
||||
}
|
||||
|
||||
func (tkn *SQLTokenizer) scanCommentType2() (TokenKind, []byte) {
|
||||
for {
|
||||
if tkn.lastChar == '*' {
|
||||
tkn.advance()
|
||||
if tkn.lastChar == '/' {
|
||||
tkn.advance()
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if tkn.lastChar == EndChar {
|
||||
tkn.setErr("unexpected EOF in comment")
|
||||
return LexError, tkn.bytes()
|
||||
}
|
||||
tkn.advance()
|
||||
}
|
||||
return Comment, tkn.bytes()
|
||||
}
|
||||
|
||||
// advance advances the tokenizer to the next rune. If the decoder encounters an error decoding, or
|
||||
// the end of the buffer is reached, tkn.lastChar will be set to EndChar. In case of a decoding
|
||||
// error, tkn.err will also be set.
|
||||
func (tkn *SQLTokenizer) advance() {
|
||||
ch, n := utf8.DecodeRune(tkn.buf[tkn.off:])
|
||||
if ch == utf8.RuneError && n < 2 {
|
||||
tkn.pos++
|
||||
tkn.lastChar = EndChar
|
||||
if n == 1 {
|
||||
tkn.setErr("invalid UTF-8 encoding beginning with 0x%x", tkn.buf[tkn.off])
|
||||
}
|
||||
return
|
||||
}
|
||||
if tkn.lastChar != 0 || tkn.pos > 0 {
|
||||
// we are past the first character
|
||||
tkn.pos += n
|
||||
}
|
||||
tkn.off += n
|
||||
tkn.lastChar = ch
|
||||
}
|
||||
|
||||
// bytes returns all the bytes that were advanced over since its last call.
|
||||
// This excludes tkn.lastChar, which will remain in the buffer
|
||||
func (tkn *SQLTokenizer) bytes() []byte {
|
||||
if tkn.lastChar == EndChar {
|
||||
ret := tkn.buf[:tkn.off]
|
||||
tkn.buf = tkn.buf[tkn.off:]
|
||||
tkn.off = 0
|
||||
return ret
|
||||
}
|
||||
lastLen := utf8.RuneLen(tkn.lastChar)
|
||||
ret := tkn.buf[:tkn.off-lastLen]
|
||||
tkn.buf = tkn.buf[tkn.off-lastLen:]
|
||||
tkn.off = lastLen
|
||||
return ret
|
||||
}
|
||||
|
||||
func isLeadingLetter(ch rune) bool {
|
||||
return unicode.IsLetter(ch) || ch == '_' || ch == '@'
|
||||
}
|
||||
|
||||
func isLetter(ch rune) bool {
|
||||
return isLeadingLetter(ch) || ch == '#'
|
||||
}
|
||||
|
||||
func digitVal(ch rune) int {
|
||||
switch {
|
||||
case '0' <= ch && ch <= '9':
|
||||
return int(ch) - '0'
|
||||
case 'a' <= ch && ch <= 'f':
|
||||
return int(ch) - 'a' + 10
|
||||
case 'A' <= ch && ch <= 'F':
|
||||
return int(ch) - 'A' + 10
|
||||
}
|
||||
return 16 // larger than any legal digit val
|
||||
}
|
||||
|
||||
func isDigit(ch rune) bool { return '0' <= ch && ch <= '9' }
|
||||
|
||||
// runeBytes converts the given rune to a slice of bytes.
|
||||
func runeBytes(r rune) []byte {
|
||||
buf := make([]byte, utf8.UTFMax)
|
||||
n := utf8.EncodeRune(buf, r)
|
||||
return buf[:n]
|
||||
}
|
||||
|
||||
// isValidCharAfterOperator returns true if c is a valid character after an operator
|
||||
func isValidCharAfterOperator(c rune) bool {
|
||||
return c == '(' || c == '`' || c == '\'' || c == '"' || c == '+' || c == '-' || unicode.IsSpace(c) || isLetter(c) || isDigit(c)
|
||||
}
|
||||
200
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/LICENSE
generated
vendored
Normal file
200
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2016-present Datadog, Inc.
|
||||
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.
|
||||
336
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs.go
generated
vendored
Normal file
336
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/configs.go
generated
vendored
Normal file
@@ -0,0 +1,336 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2022-present Datadog, Inc.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/DataDog/go-tuf/data"
|
||||
)
|
||||
|
||||
/*
|
||||
To add support for a new product:
|
||||
|
||||
1. Add the definition of the product to the const() block of products and the `allProducts` list.
|
||||
2. Define the serialized configuration struct as well as a function to parse the config from a []byte.
|
||||
3. Add the product to the `parseConfig` function
|
||||
4. Add a method on the `Repository` to retrieved typed configs for the product.
|
||||
*/
|
||||
|
||||
var allProducts = []string{ProductAPMSampling, ProductCWSDD, ProductASMFeatures, ProductASMDD, ProductASMData}
|
||||
|
||||
const (
|
||||
// ProductAPMSampling is the apm sampling product
|
||||
ProductAPMSampling = "APM_SAMPLING"
|
||||
// ProductCWSDD is the cloud workload security product managed by datadog employees
|
||||
ProductCWSDD = "CWS_DD"
|
||||
// ProductASMFeatures is the ASM product used form ASM activation through remote config
|
||||
ProductASMFeatures = "ASM_FEATURES"
|
||||
// ProductASMDD is the application security monitoring product managed by datadog employees
|
||||
ProductASMDD = "ASM_DD"
|
||||
// ProductASMData is the ASM product used to configure WAF rules data
|
||||
ProductASMData = "ASM_DATA"
|
||||
)
|
||||
|
||||
// ErrNoConfigVersion occurs when a target file's custom meta is missing the config version
|
||||
var ErrNoConfigVersion = errors.New("version missing in custom file meta")
|
||||
|
||||
func parseConfig(product string, raw []byte, metadata Metadata) (interface{}, error) {
|
||||
var c interface{}
|
||||
var err error
|
||||
switch product {
|
||||
case ProductAPMSampling:
|
||||
c, err = parseConfigAPMSampling(raw, metadata)
|
||||
case ProductASMFeatures:
|
||||
c, err = parseASMFeaturesConfig(raw, metadata)
|
||||
case ProductCWSDD:
|
||||
c, err = parseConfigCWSDD(raw, metadata)
|
||||
case ProductASMDD:
|
||||
c, err = parseConfigASMDD(raw, metadata)
|
||||
case ProductASMData:
|
||||
c, err = parseConfigASMData(raw, metadata)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown product - %s", product)
|
||||
}
|
||||
|
||||
return c, err
|
||||
}
|
||||
|
||||
// APMSamplingConfig is a deserialized APM Sampling configuration file
|
||||
// along with its associated remote config metadata.
|
||||
type APMSamplingConfig struct {
|
||||
Config []byte
|
||||
Metadata Metadata
|
||||
}
|
||||
|
||||
func parseConfigAPMSampling(data []byte, metadata Metadata) (APMSamplingConfig, error) {
|
||||
// We actually don't parse the payload here, we delegate this responsibility to the trace agent
|
||||
return APMSamplingConfig{
|
||||
Config: data,
|
||||
Metadata: metadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// APMConfigs returns the currently active APM configs
|
||||
func (r *Repository) APMConfigs() map[string]APMSamplingConfig {
|
||||
typedConfigs := make(map[string]APMSamplingConfig)
|
||||
|
||||
configs := r.getConfigs(ProductAPMSampling)
|
||||
|
||||
for path, conf := range configs {
|
||||
// We control this, so if this has gone wrong something has gone horribly wrong
|
||||
typed, ok := conf.(APMSamplingConfig)
|
||||
if !ok {
|
||||
panic("unexpected config stored as APMSamplingConfig")
|
||||
}
|
||||
|
||||
typedConfigs[path] = typed
|
||||
}
|
||||
|
||||
return typedConfigs
|
||||
}
|
||||
|
||||
// ConfigCWSDD is a deserialized CWS DD configuration file along with its
|
||||
// associated remote config metadata
|
||||
type ConfigCWSDD struct {
|
||||
Config []byte
|
||||
Metadata Metadata
|
||||
}
|
||||
|
||||
func parseConfigCWSDD(data []byte, metadata Metadata) (ConfigCWSDD, error) {
|
||||
return ConfigCWSDD{
|
||||
Config: data,
|
||||
Metadata: metadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CWSDDConfigs returns the currently active CWSDD config files
|
||||
func (r *Repository) CWSDDConfigs() map[string]ConfigCWSDD {
|
||||
typedConfigs := make(map[string]ConfigCWSDD)
|
||||
|
||||
configs := r.getConfigs(ProductCWSDD)
|
||||
|
||||
for path, conf := range configs {
|
||||
// We control this, so if this has gone wrong something has gone horribly wrong
|
||||
typed, ok := conf.(ConfigCWSDD)
|
||||
if !ok {
|
||||
panic("unexpected config stored as CWSDD Config")
|
||||
}
|
||||
|
||||
typedConfigs[path] = typed
|
||||
}
|
||||
|
||||
return typedConfigs
|
||||
}
|
||||
|
||||
// ConfigASMDD is a deserialized ASM DD configuration file along with its
|
||||
// associated remote config metadata
|
||||
type ConfigASMDD struct {
|
||||
Config []byte
|
||||
Metadata Metadata
|
||||
}
|
||||
|
||||
func parseConfigASMDD(data []byte, metadata Metadata) (ConfigASMDD, error) {
|
||||
return ConfigASMDD{
|
||||
Config: data,
|
||||
Metadata: metadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ASMDDConfigs returns the currently active ASMDD configs
|
||||
func (r *Repository) ASMDDConfigs() map[string]ConfigASMDD {
|
||||
typedConfigs := make(map[string]ConfigASMDD)
|
||||
|
||||
configs := r.getConfigs(ProductASMDD)
|
||||
|
||||
for path, conf := range configs {
|
||||
// We control this, so if this has gone wrong something has gone horribly wrong
|
||||
typed, ok := conf.(ConfigASMDD)
|
||||
if !ok {
|
||||
panic("unexpected config stored as ASMDD Config")
|
||||
}
|
||||
|
||||
typedConfigs[path] = typed
|
||||
}
|
||||
|
||||
return typedConfigs
|
||||
}
|
||||
|
||||
// ASMFeaturesConfig is a deserialized configuration file that indicates whether ASM should be enabled
|
||||
// within a tracer, along with its associated remote config metadata.
|
||||
type ASMFeaturesConfig struct {
|
||||
Config ASMFeaturesData
|
||||
Metadata Metadata
|
||||
}
|
||||
|
||||
// ASMFeaturesData describes the enabled state of ASM features
|
||||
type ASMFeaturesData struct {
|
||||
ASM struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
} `json:"asm"`
|
||||
}
|
||||
|
||||
func parseASMFeaturesConfig(data []byte, metadata Metadata) (ASMFeaturesConfig, error) {
|
||||
var f ASMFeaturesData
|
||||
|
||||
err := json.Unmarshal(data, &f)
|
||||
if err != nil {
|
||||
return ASMFeaturesConfig{}, nil
|
||||
}
|
||||
|
||||
return ASMFeaturesConfig{
|
||||
Config: f,
|
||||
Metadata: metadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ASMFeaturesConfigs returns the currently active ASMFeatures configs
|
||||
func (r *Repository) ASMFeaturesConfigs() map[string]ASMFeaturesConfig {
|
||||
typedConfigs := make(map[string]ASMFeaturesConfig)
|
||||
|
||||
configs := r.getConfigs(ProductASMFeatures)
|
||||
|
||||
for path, conf := range configs {
|
||||
// We control this, so if this has gone wrong something has gone horribly wrong
|
||||
typed, ok := conf.(ASMFeaturesConfig)
|
||||
if !ok {
|
||||
panic("unexpected config stored as ASMFeaturesConfig")
|
||||
}
|
||||
|
||||
typedConfigs[path] = typed
|
||||
}
|
||||
|
||||
return typedConfigs
|
||||
}
|
||||
|
||||
// ApplyState represents the status of a configuration application by a remote configuration client
|
||||
// Clients need to either ack the correct application of received configurations, or communicate that
|
||||
// they haven't applied it yet, or communicate any error that may have happened while doing so
|
||||
type ApplyState uint64
|
||||
|
||||
const (
|
||||
ApplyStateUnknown ApplyState = iota
|
||||
ApplyStateUnacknowledged
|
||||
ApplyStateAcknowledged
|
||||
ApplyStateError
|
||||
)
|
||||
|
||||
// ApplyStatus is the processing status for a given configuration.
|
||||
// It basically represents whether a config was successfully processed and apply, or if an error occurred
|
||||
type ApplyStatus struct {
|
||||
State ApplyState
|
||||
Error string
|
||||
}
|
||||
|
||||
// ASMDataConfig is a deserialized configuration file that holds rules data that can be used
|
||||
// by the ASM WAF for specific features (example: ip blocking).
|
||||
type ASMDataConfig struct {
|
||||
Config ASMDataRulesData
|
||||
Metadata Metadata
|
||||
}
|
||||
|
||||
// ASMDataRulesData is a serializable array of rules data entries
|
||||
type ASMDataRulesData struct {
|
||||
RulesData []ASMDataRuleData `json:"rules_data"`
|
||||
}
|
||||
|
||||
// ASMDataRuleData is an entry in the rules data list held by an ASMData configuration
|
||||
type ASMDataRuleData struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Data []ASMDataRuleDataEntry `json:"data"`
|
||||
}
|
||||
|
||||
// ASMDataRuleDataEntry represents a data entry in a rule data file
|
||||
type ASMDataRuleDataEntry struct {
|
||||
Expiration int64 `json:"expiration,omitempty"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
func parseConfigASMData(data []byte, metadata Metadata) (ASMDataConfig, error) {
|
||||
cfg := ASMDataConfig{
|
||||
Metadata: metadata,
|
||||
}
|
||||
err := json.Unmarshal(data, &cfg.Config)
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
// ASMDataConfigs returns the currently active ASMData configs
|
||||
func (r *Repository) ASMDataConfigs() map[string]ASMDataConfig {
|
||||
typedConfigs := make(map[string]ASMDataConfig)
|
||||
configs := r.getConfigs(ProductASMData)
|
||||
|
||||
for path, cfg := range configs {
|
||||
// We control this, so if this has gone wrong something has gone horribly wrong
|
||||
typed, ok := cfg.(ASMDataConfig)
|
||||
if !ok {
|
||||
panic("unexpected config stored as ASMDataConfig")
|
||||
}
|
||||
typedConfigs[path] = typed
|
||||
}
|
||||
|
||||
return typedConfigs
|
||||
}
|
||||
|
||||
// Metadata stores remote config metadata for a given configuration
|
||||
type Metadata struct {
|
||||
Product string
|
||||
ID string
|
||||
Name string
|
||||
Version uint64
|
||||
RawLength uint64
|
||||
Hashes map[string][]byte
|
||||
ApplyStatus ApplyStatus
|
||||
}
|
||||
|
||||
func newConfigMetadata(parsedPath configPath, tfm data.TargetFileMeta) (Metadata, error) {
|
||||
var m Metadata
|
||||
m.ID = parsedPath.ConfigID
|
||||
m.Product = parsedPath.Product
|
||||
m.Name = parsedPath.Name
|
||||
m.RawLength = uint64(tfm.Length)
|
||||
m.Hashes = make(map[string][]byte)
|
||||
for k, v := range tfm.Hashes {
|
||||
m.Hashes[k] = []byte(v)
|
||||
}
|
||||
v, err := fileMetaVersion(tfm)
|
||||
if err != nil {
|
||||
return Metadata{}, err
|
||||
}
|
||||
m.Version = v
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
type fileMetaCustom struct {
|
||||
Version *uint64 `json:"v"`
|
||||
}
|
||||
|
||||
func fileMetaVersion(fm data.TargetFileMeta) (uint64, error) {
|
||||
if fm.Custom == nil {
|
||||
return 0, ErrNoConfigVersion
|
||||
}
|
||||
fmc, err := parseFileMetaCustom(*fm.Custom)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return *fmc.Version, nil
|
||||
}
|
||||
|
||||
func parseFileMetaCustom(rawCustom []byte) (fileMetaCustom, error) {
|
||||
var custom fileMetaCustom
|
||||
err := json.Unmarshal(rawCustom, &custom)
|
||||
if err != nil {
|
||||
return fileMetaCustom{}, err
|
||||
}
|
||||
if custom.Version == nil {
|
||||
return fileMetaCustom{}, ErrNoConfigVersion
|
||||
}
|
||||
return custom, nil
|
||||
}
|
||||
100
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/path.go
generated
vendored
Normal file
100
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/path.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2022-present Datadog, Inc.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// matches datadog/<int>/<string>/<string>/<string> for datadog/<org_id>/<product>/<config_id>/<file>
|
||||
datadogPathRegexp = regexp.MustCompile(`^datadog/(\d+)/([^/]+)/([^/]+)/([^/]+)$`)
|
||||
datadogPathRegexpGroups = 4
|
||||
|
||||
// matches employee/<string>/<string>/<string> for employee/<org_id>/<product>/<config_id>/<file>
|
||||
employeePathRegexp = regexp.MustCompile(`^employee/([^/]+)/([^/]+)/([^/]+)$`)
|
||||
employeePathRegexpGroups = 3
|
||||
)
|
||||
|
||||
type source uint
|
||||
|
||||
const (
|
||||
sourceUnknown source = iota
|
||||
sourceDatadog
|
||||
sourceEmployee
|
||||
)
|
||||
|
||||
type configPath struct {
|
||||
Source source
|
||||
OrgID int64
|
||||
Product string
|
||||
ConfigID string
|
||||
Name string
|
||||
}
|
||||
|
||||
func parseConfigPath(path string) (configPath, error) {
|
||||
configType := parseConfigPathSource(path)
|
||||
switch configType {
|
||||
case sourceDatadog:
|
||||
return parseDatadogConfigPath(path)
|
||||
case sourceEmployee:
|
||||
return parseEmployeeConfigPath(path)
|
||||
}
|
||||
return configPath{}, fmt.Errorf("config path '%s' has unknown source", path)
|
||||
}
|
||||
|
||||
func parseDatadogConfigPath(path string) (configPath, error) {
|
||||
matchedGroups := datadogPathRegexp.FindStringSubmatch(path)
|
||||
if len(matchedGroups) != datadogPathRegexpGroups+1 {
|
||||
return configPath{}, fmt.Errorf("config file path '%s' has wrong format", path)
|
||||
}
|
||||
rawOrgID := matchedGroups[1]
|
||||
orgID, err := strconv.ParseInt(rawOrgID, 10, 64)
|
||||
if err != nil {
|
||||
return configPath{}, fmt.Errorf("could not parse orgID '%s' in config file path: %v", rawOrgID, err)
|
||||
}
|
||||
rawProduct := matchedGroups[2]
|
||||
if len(rawProduct) == 0 {
|
||||
return configPath{}, fmt.Errorf("product is empty")
|
||||
}
|
||||
return configPath{
|
||||
Source: sourceDatadog,
|
||||
OrgID: orgID,
|
||||
Product: rawProduct,
|
||||
ConfigID: matchedGroups[3],
|
||||
Name: matchedGroups[4],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseEmployeeConfigPath(path string) (configPath, error) {
|
||||
matchedGroups := employeePathRegexp.FindStringSubmatch(path)
|
||||
if len(matchedGroups) != employeePathRegexpGroups+1 {
|
||||
return configPath{}, fmt.Errorf("config file path '%s' has wrong format", path)
|
||||
}
|
||||
rawProduct := matchedGroups[1]
|
||||
if len(rawProduct) == 0 {
|
||||
return configPath{}, fmt.Errorf("product is empty")
|
||||
}
|
||||
return configPath{
|
||||
Source: sourceEmployee,
|
||||
Product: rawProduct,
|
||||
ConfigID: matchedGroups[2],
|
||||
Name: matchedGroups[3],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseConfigPathSource(path string) source {
|
||||
switch {
|
||||
case strings.HasPrefix(path, "datadog/"):
|
||||
return sourceDatadog
|
||||
case strings.HasPrefix(path, "employee/"):
|
||||
return sourceEmployee
|
||||
}
|
||||
return sourceUnknown
|
||||
}
|
||||
418
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/repository.go
generated
vendored
Normal file
418
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/repository.go
generated
vendored
Normal file
@@ -0,0 +1,418 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2022-present Datadog, Inc.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/DataDog/go-tuf/data"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrMalformedEmbeddedRoot occurs when the TUF root provided is invalid
|
||||
ErrMalformedEmbeddedRoot = errors.New("malformed embedded TUF root file provided")
|
||||
)
|
||||
|
||||
// RepositoryState contains all of the information about the current config files
|
||||
// stored by the client to be able to make an update request to an Agent
|
||||
type RepositoryState struct {
|
||||
Configs []ConfigState
|
||||
CachedFiles []CachedFile
|
||||
TargetsVersion int64
|
||||
RootsVersion int64
|
||||
OpaqueBackendState []byte
|
||||
}
|
||||
|
||||
// ConfigState describes an applied config by the agent client.
|
||||
type ConfigState struct {
|
||||
Product string
|
||||
ID string
|
||||
Version uint64
|
||||
ApplyStatus ApplyStatus
|
||||
}
|
||||
|
||||
// CachedFile describes a cached file stored by the agent client
|
||||
//
|
||||
// Note: You may be wondering why this exists when `ConfigState` exists
|
||||
// as well. The API for requesting updates does not mandate that a client
|
||||
// cache config files. This implementation just happens to do so.
|
||||
type CachedFile struct {
|
||||
Path string
|
||||
Length uint64
|
||||
Hashes map[string][]byte
|
||||
}
|
||||
|
||||
// An Update contains all the data needed to update a client's remote config repository state
|
||||
type Update struct {
|
||||
// TUFRoots contains, in order, updated roots that this repository needs to keep up with TUF validation
|
||||
TUFRoots [][]byte
|
||||
// TUFTargets is the latest TUF Targets file and is used to validate raw config files
|
||||
TUFTargets []byte
|
||||
// TargetFiles stores the raw config files by their full TUF path
|
||||
TargetFiles map[string][]byte
|
||||
// ClientcConfigs is a list of TUF path's corresponding to config files designated for this repository
|
||||
ClientConfigs []string
|
||||
}
|
||||
|
||||
// Repository is a remote config client used in a downstream process to retrieve
|
||||
// remote config updates from an Agent.
|
||||
type Repository struct {
|
||||
// TUF related data
|
||||
latestTargets *data.Targets
|
||||
tufRootsClient *tufRootsClient
|
||||
opaqueBackendState []byte
|
||||
|
||||
// Unverified mode
|
||||
tufVerificationEnabled bool
|
||||
latestRootVersion int64
|
||||
|
||||
// Config file storage
|
||||
metadata map[string]Metadata
|
||||
configs map[string]map[string]interface{}
|
||||
}
|
||||
|
||||
// NewRepository creates a new remote config repository that will track
|
||||
// both TUF metadata and raw config files for a client.
|
||||
func NewRepository(embeddedRoot []byte) (*Repository, error) {
|
||||
if embeddedRoot == nil {
|
||||
return nil, ErrMalformedEmbeddedRoot
|
||||
}
|
||||
|
||||
configs := make(map[string]map[string]interface{})
|
||||
for _, product := range allProducts {
|
||||
configs[product] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
tufRootsClient, err := newTufRootsClient(embeddedRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
latestTargets: data.NewTargets(),
|
||||
tufRootsClient: tufRootsClient,
|
||||
metadata: make(map[string]Metadata),
|
||||
configs: configs,
|
||||
tufVerificationEnabled: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewUnverifiedRepository creates a new remote config repository that will
|
||||
// track config files for a client WITHOUT verifying any TUF related metadata.
|
||||
func NewUnverifiedRepository() (*Repository, error) {
|
||||
configs := make(map[string]map[string]interface{})
|
||||
for _, product := range allProducts {
|
||||
configs[product] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
latestTargets: data.NewTargets(),
|
||||
metadata: make(map[string]Metadata),
|
||||
configs: configs,
|
||||
tufVerificationEnabled: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Update processes the ClientGetConfigsResponse from the Agent and updates the
|
||||
// configuration state
|
||||
func (r *Repository) Update(update Update) ([]string, error) {
|
||||
var err error
|
||||
var updatedTargets *data.Targets
|
||||
var tmpRootClient *tufRootsClient
|
||||
|
||||
// TUF: Update the roots and verify the TUF Targets file (optional)
|
||||
//
|
||||
// We don't want to partially update the state, so we need a temporary client to hold the new root
|
||||
// data until we know it's valid. Since verification is optional, if the repository was configured
|
||||
// to not do TUF verification we only deserialize the TUF targets file.
|
||||
if r.tufVerificationEnabled {
|
||||
tmpRootClient, err = r.tufRootsClient.clone()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = tmpRootClient.updateRoots(update.TUFRoots)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updatedTargets, err = tmpRootClient.validateTargets(update.TUFTargets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
updatedTargets, err = unsafeUnmarshalTargets(update.TUFTargets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
clientConfigsMap := make(map[string]struct{})
|
||||
for _, f := range update.ClientConfigs {
|
||||
clientConfigsMap[f] = struct{}{}
|
||||
}
|
||||
|
||||
result := newUpdateResult()
|
||||
|
||||
// 2: Check the config list and mark any missing configs as "to be removed"
|
||||
for _, configs := range r.configs {
|
||||
for path := range configs {
|
||||
if _, ok := clientConfigsMap[path]; !ok {
|
||||
result.removed = append(result.removed, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3: For all the files referenced in this update
|
||||
for _, path := range update.ClientConfigs {
|
||||
targetFileMetadata, ok := updatedTargets.Targets[path]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing config file in TUF targets - %s", path)
|
||||
}
|
||||
|
||||
// 3.a: Extract the product and ID from the path
|
||||
parsedPath, err := parseConfigPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
storedMetadata, exists := r.metadata[path]
|
||||
if exists && hashesEqual(targetFileMetadata.Hashes, storedMetadata.Hashes) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 3.d: Ensure that the raw configuration file is present in the
|
||||
// update payload.
|
||||
raw, ok := update.TargetFiles[path]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing update file - %s", path)
|
||||
}
|
||||
|
||||
// TUF: Validate the hash of the raw target file and ensure that it matches
|
||||
// the TUF metadata
|
||||
err = validateTargetFileHash(targetFileMetadata, raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error validating %s hash with TUF metadata - %v", path, err)
|
||||
}
|
||||
|
||||
// 3.e: Deserialize the configuration.
|
||||
// 3.f: Store the update details for application later
|
||||
//
|
||||
// Note: We don't have to worry about extra fields as mentioned
|
||||
// in the RFC because the encoding/json library handles that for us.
|
||||
m, err := newConfigMetadata(parsedPath, targetFileMetadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config, err := parseConfig(parsedPath.Product, raw, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.metadata[path] = m
|
||||
result.changed[parsedPath.Product][path] = config
|
||||
}
|
||||
|
||||
// 4.a: Store the new targets.signed.custom.opaque_client_state
|
||||
// TUF: Store the updated roots now that everything has validated
|
||||
if r.tufVerificationEnabled {
|
||||
r.tufRootsClient = tmpRootClient
|
||||
} else if update.TUFRoots != nil && len(update.TUFRoots) > 0 {
|
||||
v, err := extractRootVersion(update.TUFRoots[len(update.TUFRoots)-1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.latestRootVersion = v
|
||||
}
|
||||
r.latestTargets = updatedTargets
|
||||
if r.latestTargets.Custom != nil {
|
||||
r.opaqueBackendState = extractOpaqueBackendState(*r.latestTargets.Custom)
|
||||
}
|
||||
|
||||
// Upstream may not want to take any actions if the update result doesn't
|
||||
// change any configs.
|
||||
if result.isEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
changedProducts := make([]string, 0)
|
||||
for product, configs := range result.changed {
|
||||
if len(configs) > 0 {
|
||||
changedProducts = append(changedProducts, product)
|
||||
}
|
||||
}
|
||||
|
||||
// 4.b/4.rave the new state and apply cleanups
|
||||
r.applyUpdateResult(update, result)
|
||||
|
||||
return changedProducts, nil
|
||||
}
|
||||
|
||||
// UpdateApplyStatus updates the config's metadata to reflect its processing state
|
||||
// Can be used after a call to Update() in order to tell the repository which config was acked, which
|
||||
// wasn't and which errors occurred while processing.
|
||||
// Note: it is the responsibility of the caller to ensure that no new Update() call was made between
|
||||
// the first Update() call and the call to UpdateApplyStatus() so as to keep the repository state accurate.
|
||||
func (r *Repository) UpdateApplyStatus(cfgPath string, status ApplyStatus) {
|
||||
if m, ok := r.metadata[cfgPath]; ok {
|
||||
m.ApplyStatus = status
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) getConfigs(product string) map[string]interface{} {
|
||||
configs, ok := r.configs[product]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return configs
|
||||
}
|
||||
|
||||
// applyUpdateResult changes the state of the client based on the given update.
|
||||
//
|
||||
// The update is guaranteed to succeed at this point, having been vetted and the details
|
||||
// needed to apply the update stored in the `updateResult`.
|
||||
func (r *Repository) applyUpdateResult(update Update, result updateResult) {
|
||||
// 4.b Save all the updated and new config files
|
||||
for product, configs := range result.changed {
|
||||
for path, config := range configs {
|
||||
m := r.configs[product]
|
||||
m[path] = config
|
||||
}
|
||||
}
|
||||
for path, metadata := range result.metadata {
|
||||
r.metadata[path] = metadata
|
||||
}
|
||||
|
||||
// 5.b Clean up the cache of any removed configs
|
||||
for _, path := range result.removed {
|
||||
delete(r.metadata, path)
|
||||
for _, configs := range r.configs {
|
||||
delete(configs, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CurrentState returns all of the information needed to
|
||||
// make an update for new configurations.
|
||||
func (r *Repository) CurrentState() (RepositoryState, error) {
|
||||
var configs []ConfigState
|
||||
var cached []CachedFile
|
||||
|
||||
for path, metadata := range r.metadata {
|
||||
configs = append(configs, configStateFromMetadata(metadata))
|
||||
cached = append(cached, cachedFileFromMetadata(path, metadata))
|
||||
}
|
||||
|
||||
var latestRootVersion int64
|
||||
if r.tufVerificationEnabled {
|
||||
root, err := r.tufRootsClient.latestRoot()
|
||||
if err != nil {
|
||||
return RepositoryState{}, err
|
||||
}
|
||||
latestRootVersion = root.Version
|
||||
} else {
|
||||
latestRootVersion = r.latestRootVersion
|
||||
}
|
||||
|
||||
return RepositoryState{
|
||||
Configs: configs,
|
||||
CachedFiles: cached,
|
||||
TargetsVersion: r.latestTargets.Version,
|
||||
RootsVersion: latestRootVersion,
|
||||
OpaqueBackendState: r.opaqueBackendState,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// An updateResult allows the client to apply the update as a transaction
|
||||
// after validating all required preconditions
|
||||
type updateResult struct {
|
||||
removed []string
|
||||
metadata map[string]Metadata
|
||||
changed map[string]map[string]interface{}
|
||||
}
|
||||
|
||||
func newUpdateResult() updateResult {
|
||||
changed := make(map[string]map[string]interface{})
|
||||
|
||||
for _, p := range allProducts {
|
||||
changed[p] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
return updateResult{
|
||||
removed: make([]string, 0),
|
||||
metadata: make(map[string]Metadata),
|
||||
changed: changed,
|
||||
}
|
||||
}
|
||||
|
||||
func (ur updateResult) Log() {
|
||||
log.Printf("Removed Configs: %v", ur.removed)
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("Changed configs: [")
|
||||
for path := range ur.metadata {
|
||||
b.WriteString(path)
|
||||
b.WriteString(" ")
|
||||
}
|
||||
b.WriteString("]")
|
||||
|
||||
log.Println(b.String())
|
||||
}
|
||||
|
||||
func (ur updateResult) isEmpty() bool {
|
||||
return len(ur.removed) == 0 && len(ur.metadata) == 0
|
||||
}
|
||||
|
||||
func configStateFromMetadata(m Metadata) ConfigState {
|
||||
return ConfigState{
|
||||
Product: m.Product,
|
||||
ID: m.ID,
|
||||
Version: m.Version,
|
||||
ApplyStatus: m.ApplyStatus,
|
||||
}
|
||||
}
|
||||
|
||||
func cachedFileFromMetadata(path string, m Metadata) CachedFile {
|
||||
return CachedFile{
|
||||
Path: path,
|
||||
Length: m.RawLength,
|
||||
Hashes: m.Hashes,
|
||||
}
|
||||
}
|
||||
|
||||
// hashesEqual checks if the hash values in the TUF metadata file match the stored
|
||||
// hash values for a given config
|
||||
func hashesEqual(tufHashes data.Hashes, storedHashes map[string][]byte) bool {
|
||||
for algorithm, value := range tufHashes {
|
||||
v, ok := storedHashes[algorithm]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if !bytes.Equal(value, v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func extractOpaqueBackendState(targetsCustom []byte) []byte {
|
||||
state := struct {
|
||||
State []byte `json:"opaque_backend_state"`
|
||||
}{nil}
|
||||
|
||||
err := json.Unmarshal(targetsCustom, &state)
|
||||
if err != nil {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
return state.State
|
||||
}
|
||||
233
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/tuf.go
generated
vendored
Normal file
233
vendor/github.com/DataDog/datadog-agent/pkg/remoteconfig/state/tuf.go
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2022-present Datadog, Inc.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/DataDog/go-tuf/client"
|
||||
"github.com/DataDog/go-tuf/data"
|
||||
"github.com/DataDog/go-tuf/util"
|
||||
"github.com/DataDog/go-tuf/verify"
|
||||
)
|
||||
|
||||
type tufRootsClient struct {
|
||||
rootClient *client.Client
|
||||
rootLocalStore client.LocalStore
|
||||
rootRemoteStore *rootClientRemoteStore
|
||||
}
|
||||
|
||||
func newTufRootsClient(root []byte) (*tufRootsClient, error) {
|
||||
rootLocalStore := client.MemoryLocalStore()
|
||||
rootRemoteStore := &rootClientRemoteStore{}
|
||||
rootClient := client.NewClient(rootLocalStore, rootRemoteStore)
|
||||
|
||||
err := rootClient.InitLocal(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tufRootsClient{
|
||||
rootClient: rootClient,
|
||||
rootLocalStore: rootLocalStore,
|
||||
rootRemoteStore: rootRemoteStore,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (trc *tufRootsClient) clone() (*tufRootsClient, error) {
|
||||
root, err := trc.latestRootRaw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newTufRootsClient(root)
|
||||
}
|
||||
|
||||
func (trc *tufRootsClient) updateRoots(newRoots [][]byte) error {
|
||||
if len(newRoots) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
trc.rootRemoteStore.roots = append(trc.rootRemoteStore.roots, newRoots...)
|
||||
|
||||
return trc.rootClient.UpdateRoots()
|
||||
}
|
||||
|
||||
func (trc *tufRootsClient) latestRoot() (*data.Root, error) {
|
||||
raw, err := trc.latestRootRaw()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return unsafeUnmarshalRoot(raw)
|
||||
}
|
||||
|
||||
func (trc *tufRootsClient) latestRootRaw() ([]byte, error) {
|
||||
metas, err := trc.rootLocalStore.GetMeta()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawRoot := metas["root.json"]
|
||||
|
||||
return rawRoot, nil
|
||||
}
|
||||
|
||||
func (trc *tufRootsClient) validateTargets(rawTargets []byte) (*data.Targets, error) {
|
||||
root, err := trc.latestRoot()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := verify.NewDB()
|
||||
for _, key := range root.Keys {
|
||||
for _, id := range key.IDs() {
|
||||
if err := db.AddKey(id, key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
targetsRole, hasRoleTargets := root.Roles["targets"]
|
||||
if !hasRoleTargets {
|
||||
return nil, fmt.Errorf("root is missing a targets role")
|
||||
}
|
||||
role := &data.Role{Threshold: targetsRole.Threshold, KeyIDs: targetsRole.KeyIDs}
|
||||
if err := db.AddRole("targets", role); err != nil {
|
||||
return nil, fmt.Errorf("could not add targets role to db: %v", err)
|
||||
}
|
||||
var targets data.Targets
|
||||
err = db.Unmarshal(rawTargets, &targets, "targets", 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &targets, nil
|
||||
}
|
||||
|
||||
type rootClientRemoteStore struct {
|
||||
roots [][]byte
|
||||
}
|
||||
|
||||
func (s *rootClientRemoteStore) GetMeta(name string) (stream io.ReadCloser, size int64, err error) {
|
||||
metaPath, err := parseMetaPath(name)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if metaPath.role != roleRoot || !metaPath.versionSet {
|
||||
return nil, 0, client.ErrNotFound{File: name}
|
||||
}
|
||||
for _, root := range s.roots {
|
||||
parsedRoot, err := unsafeUnmarshalRoot(root)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if parsedRoot.Version == metaPath.version {
|
||||
return io.NopCloser(bytes.NewReader(root)), int64(len(root)), nil
|
||||
}
|
||||
}
|
||||
return nil, 0, client.ErrNotFound{File: name}
|
||||
}
|
||||
|
||||
func (s *rootClientRemoteStore) GetTarget(path string) (stream io.ReadCloser, size int64, err error) {
|
||||
return nil, 0, client.ErrNotFound{File: path}
|
||||
}
|
||||
|
||||
type role string
|
||||
|
||||
const (
|
||||
roleRoot role = "root"
|
||||
)
|
||||
|
||||
type metaPath struct {
|
||||
role role
|
||||
version int64
|
||||
versionSet bool
|
||||
}
|
||||
|
||||
func parseMetaPath(rawMetaPath string) (metaPath, error) {
|
||||
splitRawMetaPath := strings.SplitN(rawMetaPath, ".", 3)
|
||||
if len(splitRawMetaPath) != 2 && len(splitRawMetaPath) != 3 {
|
||||
return metaPath{}, fmt.Errorf("invalid metadata path '%s'", rawMetaPath)
|
||||
}
|
||||
suffix := splitRawMetaPath[len(splitRawMetaPath)-1]
|
||||
if suffix != "json" {
|
||||
return metaPath{}, fmt.Errorf("invalid metadata path (suffix) '%s'", rawMetaPath)
|
||||
}
|
||||
rawRole := splitRawMetaPath[len(splitRawMetaPath)-2]
|
||||
if rawRole == "" {
|
||||
return metaPath{}, fmt.Errorf("invalid metadata path (role) '%s'", rawMetaPath)
|
||||
}
|
||||
if len(splitRawMetaPath) == 2 {
|
||||
return metaPath{
|
||||
role: role(rawRole),
|
||||
}, nil
|
||||
}
|
||||
rawVersion, err := strconv.ParseInt(splitRawMetaPath[0], 10, 64)
|
||||
if err != nil {
|
||||
return metaPath{}, fmt.Errorf("invalid metadata path (version) '%s': %w", rawMetaPath, err)
|
||||
}
|
||||
return metaPath{
|
||||
role: role(rawRole),
|
||||
version: rawVersion,
|
||||
versionSet: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateTargetFileHash(targetMeta data.TargetFileMeta, targetFile []byte) error {
|
||||
if len(targetMeta.HashAlgorithms()) == 0 {
|
||||
return fmt.Errorf("target file has no hash")
|
||||
}
|
||||
generatedMeta, err := util.GenerateFileMeta(bytes.NewBuffer(targetFile), targetMeta.HashAlgorithms()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = util.FileMetaEqual(targetMeta.FileMeta, generatedMeta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unsafeUnmarshalRoot(raw []byte) (*data.Root, error) {
|
||||
var signedRoot data.Signed
|
||||
err := json.Unmarshal(raw, &signedRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var root data.Root
|
||||
err = json.Unmarshal(signedRoot.Signed, &root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &root, err
|
||||
}
|
||||
|
||||
func unsafeUnmarshalTargets(raw []byte) (*data.Targets, error) {
|
||||
var signedTargets data.Signed
|
||||
err := json.Unmarshal(raw, &signedTargets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var targets data.Targets
|
||||
err = json.Unmarshal(signedTargets.Signed, &targets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &targets, err
|
||||
}
|
||||
|
||||
func extractRootVersion(raw []byte) (int64, error) {
|
||||
root, err := unsafeUnmarshalRoot(raw)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return root.Version, nil
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 go-logfmt
|
||||
Copyright (c) 2015 Datadog, Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -19,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
4
vendor/github.com/DataDog/datadog-go/statsd/README.md
generated
vendored
Normal file
4
vendor/github.com/DataDog/datadog-go/statsd/README.md
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
## Overview
|
||||
|
||||
Package `statsd` provides a Go [dogstatsd](http://docs.datadoghq.com/guides/dogstatsd/) client. Dogstatsd extends Statsd, adding tags
|
||||
and histograms.
|
||||
283
vendor/github.com/DataDog/datadog-go/statsd/aggregator.go
generated
vendored
Normal file
283
vendor/github.com/DataDog/datadog-go/statsd/aggregator.go
generated
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
countsMap map[string]*countMetric
|
||||
gaugesMap map[string]*gaugeMetric
|
||||
setsMap map[string]*setMetric
|
||||
bufferedMetricMap map[string]*bufferedMetric
|
||||
)
|
||||
|
||||
type aggregator struct {
|
||||
nbContextGauge int32
|
||||
nbContextCount int32
|
||||
nbContextSet int32
|
||||
|
||||
countsM sync.RWMutex
|
||||
gaugesM sync.RWMutex
|
||||
setsM sync.RWMutex
|
||||
|
||||
gauges gaugesMap
|
||||
counts countsMap
|
||||
sets setsMap
|
||||
histograms bufferedMetricContexts
|
||||
distributions bufferedMetricContexts
|
||||
timings bufferedMetricContexts
|
||||
|
||||
closed chan struct{}
|
||||
|
||||
client *Client
|
||||
|
||||
// aggregator implements ChannelMode mechanism to receive histograms,
|
||||
// distributions and timings. Since they need sampling they need to
|
||||
// lock for random. When using both ChannelMode and ExtendedAggregation
|
||||
// we don't want goroutine to fight over the lock.
|
||||
inputMetrics chan metric
|
||||
stopChannelMode chan struct{}
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
type aggregatorMetrics struct {
|
||||
nbContext int32
|
||||
nbContextGauge int32
|
||||
nbContextCount int32
|
||||
nbContextSet int32
|
||||
nbContextHistogram int32
|
||||
nbContextDistribution int32
|
||||
nbContextTiming int32
|
||||
}
|
||||
|
||||
func newAggregator(c *Client) *aggregator {
|
||||
return &aggregator{
|
||||
client: c,
|
||||
counts: countsMap{},
|
||||
gauges: gaugesMap{},
|
||||
sets: setsMap{},
|
||||
histograms: newBufferedContexts(newHistogramMetric),
|
||||
distributions: newBufferedContexts(newDistributionMetric),
|
||||
timings: newBufferedContexts(newTimingMetric),
|
||||
closed: make(chan struct{}),
|
||||
stopChannelMode: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregator) start(flushInterval time.Duration) {
|
||||
ticker := time.NewTicker(flushInterval)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
a.flush()
|
||||
case <-a.closed:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (a *aggregator) startReceivingMetric(bufferSize int, nbWorkers int) {
|
||||
a.inputMetrics = make(chan metric, bufferSize)
|
||||
for i := 0; i < nbWorkers; i++ {
|
||||
a.wg.Add(1)
|
||||
go a.pullMetric()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregator) stopReceivingMetric() {
|
||||
close(a.stopChannelMode)
|
||||
a.wg.Wait()
|
||||
}
|
||||
|
||||
func (a *aggregator) stop() {
|
||||
a.closed <- struct{}{}
|
||||
}
|
||||
|
||||
func (a *aggregator) pullMetric() {
|
||||
for {
|
||||
select {
|
||||
case m := <-a.inputMetrics:
|
||||
switch m.metricType {
|
||||
case histogram:
|
||||
a.histogram(m.name, m.fvalue, m.tags, m.rate)
|
||||
case distribution:
|
||||
a.distribution(m.name, m.fvalue, m.tags, m.rate)
|
||||
case timing:
|
||||
a.timing(m.name, m.fvalue, m.tags, m.rate)
|
||||
}
|
||||
case <-a.stopChannelMode:
|
||||
a.wg.Done()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregator) flush() {
|
||||
for _, m := range a.flushMetrics() {
|
||||
a.client.sendBlocking(m)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregator) flushTelemetryMetrics() *aggregatorMetrics {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
am := &aggregatorMetrics{
|
||||
nbContextGauge: atomic.SwapInt32(&a.nbContextGauge, 0),
|
||||
nbContextCount: atomic.SwapInt32(&a.nbContextCount, 0),
|
||||
nbContextSet: atomic.SwapInt32(&a.nbContextSet, 0),
|
||||
nbContextHistogram: a.histograms.resetAndGetNbContext(),
|
||||
nbContextDistribution: a.distributions.resetAndGetNbContext(),
|
||||
nbContextTiming: a.timings.resetAndGetNbContext(),
|
||||
}
|
||||
|
||||
am.nbContext = am.nbContextGauge + am.nbContextCount + am.nbContextSet + am.nbContextHistogram + am.nbContextDistribution + am.nbContextTiming
|
||||
return am
|
||||
}
|
||||
|
||||
func (a *aggregator) flushMetrics() []metric {
|
||||
metrics := []metric{}
|
||||
|
||||
// We reset the values to avoid sending 'zero' values for metrics not
|
||||
// sampled during this flush interval
|
||||
|
||||
a.setsM.Lock()
|
||||
sets := a.sets
|
||||
a.sets = setsMap{}
|
||||
a.setsM.Unlock()
|
||||
|
||||
for _, s := range sets {
|
||||
metrics = append(metrics, s.flushUnsafe()...)
|
||||
}
|
||||
|
||||
a.gaugesM.Lock()
|
||||
gauges := a.gauges
|
||||
a.gauges = gaugesMap{}
|
||||
a.gaugesM.Unlock()
|
||||
|
||||
for _, g := range gauges {
|
||||
metrics = append(metrics, g.flushUnsafe())
|
||||
}
|
||||
|
||||
a.countsM.Lock()
|
||||
counts := a.counts
|
||||
a.counts = countsMap{}
|
||||
a.countsM.Unlock()
|
||||
|
||||
for _, c := range counts {
|
||||
metrics = append(metrics, c.flushUnsafe())
|
||||
}
|
||||
|
||||
metrics = a.histograms.flush(metrics)
|
||||
metrics = a.distributions.flush(metrics)
|
||||
metrics = a.timings.flush(metrics)
|
||||
|
||||
atomic.AddInt32(&a.nbContextCount, int32(len(counts)))
|
||||
atomic.AddInt32(&a.nbContextGauge, int32(len(gauges)))
|
||||
atomic.AddInt32(&a.nbContextSet, int32(len(sets)))
|
||||
return metrics
|
||||
}
|
||||
|
||||
func getContext(name string, tags []string) string {
|
||||
return name + ":" + strings.Join(tags, tagSeparatorSymbol)
|
||||
}
|
||||
|
||||
func getContextAndTags(name string, tags []string) (string, string) {
|
||||
stringTags := strings.Join(tags, tagSeparatorSymbol)
|
||||
return name + ":" + stringTags, stringTags
|
||||
}
|
||||
|
||||
func (a *aggregator) count(name string, value int64, tags []string) error {
|
||||
context := getContext(name, tags)
|
||||
a.countsM.RLock()
|
||||
if count, found := a.counts[context]; found {
|
||||
count.sample(value)
|
||||
a.countsM.RUnlock()
|
||||
return nil
|
||||
}
|
||||
a.countsM.RUnlock()
|
||||
|
||||
a.countsM.Lock()
|
||||
// Check if another goroutines hasn't created the value betwen the RUnlock and 'Lock'
|
||||
if count, found := a.counts[context]; found {
|
||||
count.sample(value)
|
||||
a.countsM.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
a.counts[context] = newCountMetric(name, value, tags)
|
||||
a.countsM.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aggregator) gauge(name string, value float64, tags []string) error {
|
||||
context := getContext(name, tags)
|
||||
a.gaugesM.RLock()
|
||||
if gauge, found := a.gauges[context]; found {
|
||||
gauge.sample(value)
|
||||
a.gaugesM.RUnlock()
|
||||
return nil
|
||||
}
|
||||
a.gaugesM.RUnlock()
|
||||
|
||||
gauge := newGaugeMetric(name, value, tags)
|
||||
|
||||
a.gaugesM.Lock()
|
||||
// Check if another goroutines hasn't created the value betwen the 'RUnlock' and 'Lock'
|
||||
if gauge, found := a.gauges[context]; found {
|
||||
gauge.sample(value)
|
||||
a.gaugesM.Unlock()
|
||||
return nil
|
||||
}
|
||||
a.gauges[context] = gauge
|
||||
a.gaugesM.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aggregator) set(name string, value string, tags []string) error {
|
||||
context := getContext(name, tags)
|
||||
a.setsM.RLock()
|
||||
if set, found := a.sets[context]; found {
|
||||
set.sample(value)
|
||||
a.setsM.RUnlock()
|
||||
return nil
|
||||
}
|
||||
a.setsM.RUnlock()
|
||||
|
||||
a.setsM.Lock()
|
||||
// Check if another goroutines hasn't created the value betwen the 'RUnlock' and 'Lock'
|
||||
if set, found := a.sets[context]; found {
|
||||
set.sample(value)
|
||||
a.setsM.Unlock()
|
||||
return nil
|
||||
}
|
||||
a.sets[context] = newSetMetric(name, value, tags)
|
||||
a.setsM.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Only histograms, distributions and timings are sampled with a rate since we
|
||||
// only pack them in on message instead of aggregating them. Discarding the
|
||||
// sample rate will have impacts on the CPU and memory usage of the Agent.
|
||||
|
||||
// type alias for Client.sendToAggregator
|
||||
type bufferedMetricSampleFunc func(name string, value float64, tags []string, rate float64) error
|
||||
|
||||
func (a *aggregator) histogram(name string, value float64, tags []string, rate float64) error {
|
||||
return a.histograms.sample(name, value, tags, rate)
|
||||
}
|
||||
|
||||
func (a *aggregator) distribution(name string, value float64, tags []string, rate float64) error {
|
||||
return a.distributions.sample(name, value, tags, rate)
|
||||
}
|
||||
|
||||
func (a *aggregator) timing(name string, value float64, tags []string, rate float64) error {
|
||||
return a.timings.sample(name, value, tags, rate)
|
||||
}
|
||||
190
vendor/github.com/DataDog/datadog-go/statsd/buffer.go
generated
vendored
Normal file
190
vendor/github.com/DataDog/datadog-go/statsd/buffer.go
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type bufferFullError string
|
||||
|
||||
func (e bufferFullError) Error() string { return string(e) }
|
||||
|
||||
const errBufferFull = bufferFullError("statsd buffer is full")
|
||||
|
||||
type partialWriteError string
|
||||
|
||||
func (e partialWriteError) Error() string { return string(e) }
|
||||
|
||||
const errPartialWrite = partialWriteError("value partially written")
|
||||
|
||||
const metricOverhead = 512
|
||||
|
||||
// statsdBuffer is a buffer containing statsd messages
|
||||
// this struct methods are NOT safe for concurent use
|
||||
type statsdBuffer struct {
|
||||
buffer []byte
|
||||
maxSize int
|
||||
maxElements int
|
||||
elementCount int
|
||||
}
|
||||
|
||||
func newStatsdBuffer(maxSize, maxElements int) *statsdBuffer {
|
||||
return &statsdBuffer{
|
||||
buffer: make([]byte, 0, maxSize+metricOverhead), // pre-allocate the needed size + metricOverhead to avoid having Go re-allocate on it's own if an element does not fit
|
||||
maxSize: maxSize,
|
||||
maxElements: maxElements,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeGauge(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendGauge(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeCount(namespace string, globalTags []string, name string, value int64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendCount(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeHistogram(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendHistogram(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
// writeAggregated serialized as many values as possible in the current buffer and return the position in values where it stopped.
|
||||
func (b *statsdBuffer) writeAggregated(metricSymbol []byte, namespace string, globalTags []string, name string, values []float64, tags string, tagSize int, precision int) (int, error) {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return 0, errBufferFull
|
||||
}
|
||||
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendHeader(b.buffer, namespace, name)
|
||||
|
||||
// buffer already full
|
||||
if len(b.buffer)+tagSize > b.maxSize {
|
||||
b.buffer = originalBuffer
|
||||
return 0, errBufferFull
|
||||
}
|
||||
|
||||
// We add as many value as possible
|
||||
var position int
|
||||
for idx, v := range values {
|
||||
previousBuffer := b.buffer
|
||||
if idx != 0 {
|
||||
b.buffer = append(b.buffer, ':')
|
||||
}
|
||||
|
||||
b.buffer = strconv.AppendFloat(b.buffer, v, 'f', precision, 64)
|
||||
|
||||
// Should we stop serializing and switch to another buffer
|
||||
if len(b.buffer)+tagSize > b.maxSize {
|
||||
b.buffer = previousBuffer
|
||||
break
|
||||
}
|
||||
position = idx + 1
|
||||
}
|
||||
|
||||
// we could not add a single value
|
||||
if position == 0 {
|
||||
b.buffer = originalBuffer
|
||||
return 0, errBufferFull
|
||||
}
|
||||
|
||||
b.buffer = append(b.buffer, '|')
|
||||
b.buffer = append(b.buffer, metricSymbol...)
|
||||
b.buffer = appendTagsAggregated(b.buffer, globalTags, tags)
|
||||
b.writeSeparator()
|
||||
b.elementCount++
|
||||
|
||||
if position != len(values) {
|
||||
return position, errPartialWrite
|
||||
}
|
||||
return position, nil
|
||||
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeDistribution(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendDistribution(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeSet(namespace string, globalTags []string, name string, value string, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendSet(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeTiming(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendTiming(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeEvent(event Event, globalTags []string) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendEvent(b.buffer, event, globalTags)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeServiceCheck(serviceCheck ServiceCheck, globalTags []string) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendServiceCheck(b.buffer, serviceCheck, globalTags)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) validateNewElement(originalBuffer []byte) error {
|
||||
if len(b.buffer) > b.maxSize {
|
||||
b.buffer = originalBuffer
|
||||
return errBufferFull
|
||||
}
|
||||
b.elementCount++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeSeparator() {
|
||||
b.buffer = append(b.buffer, '\n')
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) reset() {
|
||||
b.buffer = b.buffer[:0]
|
||||
b.elementCount = 0
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) bytes() []byte {
|
||||
return b.buffer
|
||||
}
|
||||
40
vendor/github.com/DataDog/datadog-go/statsd/buffer_pool.go
generated
vendored
Normal file
40
vendor/github.com/DataDog/datadog-go/statsd/buffer_pool.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package statsd
|
||||
|
||||
type bufferPool struct {
|
||||
pool chan *statsdBuffer
|
||||
bufferMaxSize int
|
||||
bufferMaxElements int
|
||||
}
|
||||
|
||||
func newBufferPool(poolSize, bufferMaxSize, bufferMaxElements int) *bufferPool {
|
||||
p := &bufferPool{
|
||||
pool: make(chan *statsdBuffer, poolSize),
|
||||
bufferMaxSize: bufferMaxSize,
|
||||
bufferMaxElements: bufferMaxElements,
|
||||
}
|
||||
for i := 0; i < poolSize; i++ {
|
||||
p.addNewBuffer()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *bufferPool) addNewBuffer() {
|
||||
p.pool <- newStatsdBuffer(p.bufferMaxSize, p.bufferMaxElements)
|
||||
}
|
||||
|
||||
func (p *bufferPool) borrowBuffer() *statsdBuffer {
|
||||
select {
|
||||
case b := <-p.pool:
|
||||
return b
|
||||
default:
|
||||
return newStatsdBuffer(p.bufferMaxSize, p.bufferMaxElements)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bufferPool) returnBuffer(buffer *statsdBuffer) {
|
||||
buffer.reset()
|
||||
select {
|
||||
case p.pool <- buffer:
|
||||
default:
|
||||
}
|
||||
}
|
||||
82
vendor/github.com/DataDog/datadog-go/statsd/buffered_metric_context.go
generated
vendored
Normal file
82
vendor/github.com/DataDog/datadog-go/statsd/buffered_metric_context.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// bufferedMetricContexts represent the contexts for Histograms, Distributions
|
||||
// and Timing. Since those 3 metric types behave the same way and are sampled
|
||||
// with the same type they're represented by the same class.
|
||||
type bufferedMetricContexts struct {
|
||||
nbContext int32
|
||||
mutex sync.RWMutex
|
||||
values bufferedMetricMap
|
||||
newMetric func(string, float64, string) *bufferedMetric
|
||||
|
||||
// Each bufferedMetricContexts uses its own random source and random
|
||||
// lock to prevent goroutines from contending for the lock on the
|
||||
// "math/rand" package-global random source (e.g. calls like
|
||||
// "rand.Float64()" must acquire a shared lock to get the next
|
||||
// pseudorandom number).
|
||||
random *rand.Rand
|
||||
randomLock sync.Mutex
|
||||
}
|
||||
|
||||
func newBufferedContexts(newMetric func(string, float64, string) *bufferedMetric) bufferedMetricContexts {
|
||||
return bufferedMetricContexts{
|
||||
values: bufferedMetricMap{},
|
||||
newMetric: newMetric,
|
||||
// Note that calling "time.Now().UnixNano()" repeatedly quickly may return
|
||||
// very similar values. That's fine for seeding the worker-specific random
|
||||
// source because we just need an evenly distributed stream of float values.
|
||||
// Do not use this random source for cryptographic randomness.
|
||||
random: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
}
|
||||
}
|
||||
|
||||
func (bc *bufferedMetricContexts) flush(metrics []metric) []metric {
|
||||
bc.mutex.Lock()
|
||||
values := bc.values
|
||||
bc.values = bufferedMetricMap{}
|
||||
bc.mutex.Unlock()
|
||||
|
||||
for _, d := range values {
|
||||
metrics = append(metrics, d.flushUnsafe())
|
||||
}
|
||||
atomic.AddInt32(&bc.nbContext, int32(len(values)))
|
||||
return metrics
|
||||
}
|
||||
|
||||
func (bc *bufferedMetricContexts) sample(name string, value float64, tags []string, rate float64) error {
|
||||
if !shouldSample(rate, bc.random, &bc.randomLock) {
|
||||
return nil
|
||||
}
|
||||
|
||||
context, stringTags := getContextAndTags(name, tags)
|
||||
|
||||
bc.mutex.RLock()
|
||||
if v, found := bc.values[context]; found {
|
||||
v.sample(value)
|
||||
bc.mutex.RUnlock()
|
||||
return nil
|
||||
}
|
||||
bc.mutex.RUnlock()
|
||||
|
||||
bc.mutex.Lock()
|
||||
// Check if another goroutines hasn't created the value betwen the 'RUnlock' and 'Lock'
|
||||
if v, found := bc.values[context]; found {
|
||||
v.sample(value)
|
||||
bc.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
bc.values[context] = bc.newMetric(name, value, stringTags)
|
||||
bc.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bc *bufferedMetricContexts) resetAndGetNbContext() int32 {
|
||||
return atomic.SwapInt32(&bc.nbContext, 0)
|
||||
}
|
||||
91
vendor/github.com/DataDog/datadog-go/statsd/event.go
generated
vendored
Normal file
91
vendor/github.com/DataDog/datadog-go/statsd/event.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Events support
|
||||
// EventAlertType and EventAlertPriority became exported types after this issue was submitted: https://github.com/DataDog/datadog-go/issues/41
|
||||
// The reason why they got exported is so that client code can directly use the types.
|
||||
|
||||
// EventAlertType is the alert type for events
|
||||
type EventAlertType string
|
||||
|
||||
const (
|
||||
// Info is the "info" AlertType for events
|
||||
Info EventAlertType = "info"
|
||||
// Error is the "error" AlertType for events
|
||||
Error EventAlertType = "error"
|
||||
// Warning is the "warning" AlertType for events
|
||||
Warning EventAlertType = "warning"
|
||||
// Success is the "success" AlertType for events
|
||||
Success EventAlertType = "success"
|
||||
)
|
||||
|
||||
// EventPriority is the event priority for events
|
||||
type EventPriority string
|
||||
|
||||
const (
|
||||
// Normal is the "normal" Priority for events
|
||||
Normal EventPriority = "normal"
|
||||
// Low is the "low" Priority for events
|
||||
Low EventPriority = "low"
|
||||
)
|
||||
|
||||
// An Event is an object that can be posted to your DataDog event stream.
|
||||
type Event struct {
|
||||
// Title of the event. Required.
|
||||
Title string
|
||||
// Text is the description of the event. Required.
|
||||
Text string
|
||||
// Timestamp is a timestamp for the event. If not provided, the dogstatsd
|
||||
// server will set this to the current time.
|
||||
Timestamp time.Time
|
||||
// Hostname for the event.
|
||||
Hostname string
|
||||
// AggregationKey groups this event with others of the same key.
|
||||
AggregationKey string
|
||||
// Priority of the event. Can be statsd.Low or statsd.Normal.
|
||||
Priority EventPriority
|
||||
// SourceTypeName is a source type for the event.
|
||||
SourceTypeName string
|
||||
// AlertType can be statsd.Info, statsd.Error, statsd.Warning, or statsd.Success.
|
||||
// If absent, the default value applied by the dogstatsd server is Info.
|
||||
AlertType EventAlertType
|
||||
// Tags for the event.
|
||||
Tags []string
|
||||
}
|
||||
|
||||
// NewEvent creates a new event with the given title and text. Error checking
|
||||
// against these values is done at send-time, or upon running e.Check.
|
||||
func NewEvent(title, text string) *Event {
|
||||
return &Event{
|
||||
Title: title,
|
||||
Text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Check verifies that an event is valid.
|
||||
func (e Event) Check() error {
|
||||
if len(e.Title) == 0 {
|
||||
return fmt.Errorf("statsd.Event title is required")
|
||||
}
|
||||
if len(e.Text) == 0 {
|
||||
return fmt.Errorf("statsd.Event text is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode returns the dogstatsd wire protocol representation for an event.
|
||||
// Tags may be passed which will be added to the encoded output but not to
|
||||
// the Event's list of tags, eg. for default tags.
|
||||
func (e Event) Encode(tags ...string) (string, error) {
|
||||
err := e.Check()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buffer []byte
|
||||
buffer = appendEvent(buffer, e, tags)
|
||||
return string(buffer), nil
|
||||
}
|
||||
39
vendor/github.com/DataDog/datadog-go/statsd/fnv1a.go
generated
vendored
Normal file
39
vendor/github.com/DataDog/datadog-go/statsd/fnv1a.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package statsd
|
||||
|
||||
const (
|
||||
// FNV-1a
|
||||
offset32 = uint32(2166136261)
|
||||
prime32 = uint32(16777619)
|
||||
|
||||
// init32 is what 32 bits hash values should be initialized with.
|
||||
init32 = offset32
|
||||
)
|
||||
|
||||
// HashString32 returns the hash of s.
|
||||
func hashString32(s string) uint32 {
|
||||
return addString32(init32, s)
|
||||
}
|
||||
|
||||
// AddString32 adds the hash of s to the precomputed hash value h.
|
||||
func addString32(h uint32, s string) uint32 {
|
||||
i := 0
|
||||
n := (len(s) / 8) * 8
|
||||
|
||||
for i != n {
|
||||
h = (h ^ uint32(s[i])) * prime32
|
||||
h = (h ^ uint32(s[i+1])) * prime32
|
||||
h = (h ^ uint32(s[i+2])) * prime32
|
||||
h = (h ^ uint32(s[i+3])) * prime32
|
||||
h = (h ^ uint32(s[i+4])) * prime32
|
||||
h = (h ^ uint32(s[i+5])) * prime32
|
||||
h = (h ^ uint32(s[i+6])) * prime32
|
||||
h = (h ^ uint32(s[i+7])) * prime32
|
||||
i += 8
|
||||
}
|
||||
|
||||
for _, c := range s[i:] {
|
||||
h = (h ^ uint32(c)) * prime32
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
257
vendor/github.com/DataDog/datadog-go/statsd/format.go
generated
vendored
Normal file
257
vendor/github.com/DataDog/datadog-go/statsd/format.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
gaugeSymbol = []byte("g")
|
||||
countSymbol = []byte("c")
|
||||
histogramSymbol = []byte("h")
|
||||
distributionSymbol = []byte("d")
|
||||
setSymbol = []byte("s")
|
||||
timingSymbol = []byte("ms")
|
||||
tagSeparatorSymbol = ","
|
||||
)
|
||||
|
||||
func appendHeader(buffer []byte, namespace string, name string) []byte {
|
||||
if namespace != "" {
|
||||
buffer = append(buffer, namespace...)
|
||||
}
|
||||
buffer = append(buffer, name...)
|
||||
buffer = append(buffer, ':')
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendRate(buffer []byte, rate float64) []byte {
|
||||
if rate < 1 {
|
||||
buffer = append(buffer, "|@"...)
|
||||
buffer = strconv.AppendFloat(buffer, rate, 'f', -1, 64)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendWithoutNewlines(buffer []byte, s string) []byte {
|
||||
// fastpath for strings without newlines
|
||||
if strings.IndexByte(s, '\n') == -1 {
|
||||
return append(buffer, s...)
|
||||
}
|
||||
|
||||
for _, b := range []byte(s) {
|
||||
if b != '\n' {
|
||||
buffer = append(buffer, b)
|
||||
}
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendTags(buffer []byte, globalTags []string, tags []string) []byte {
|
||||
if len(globalTags) == 0 && len(tags) == 0 {
|
||||
return buffer
|
||||
}
|
||||
buffer = append(buffer, "|#"...)
|
||||
firstTag := true
|
||||
|
||||
for _, tag := range globalTags {
|
||||
if !firstTag {
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
}
|
||||
buffer = appendWithoutNewlines(buffer, tag)
|
||||
firstTag = false
|
||||
}
|
||||
for _, tag := range tags {
|
||||
if !firstTag {
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
}
|
||||
buffer = appendWithoutNewlines(buffer, tag)
|
||||
firstTag = false
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendTagsAggregated(buffer []byte, globalTags []string, tags string) []byte {
|
||||
if len(globalTags) == 0 && tags == "" {
|
||||
return buffer
|
||||
}
|
||||
|
||||
buffer = append(buffer, "|#"...)
|
||||
firstTag := true
|
||||
|
||||
for _, tag := range globalTags {
|
||||
if !firstTag {
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
}
|
||||
buffer = appendWithoutNewlines(buffer, tag)
|
||||
firstTag = false
|
||||
}
|
||||
if tags != "" {
|
||||
if !firstTag {
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
}
|
||||
buffer = appendWithoutNewlines(buffer, tags)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendFloatMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64, precision int) []byte {
|
||||
buffer = appendHeader(buffer, namespace, name)
|
||||
buffer = strconv.AppendFloat(buffer, value, 'f', precision, 64)
|
||||
buffer = append(buffer, '|')
|
||||
buffer = append(buffer, typeSymbol...)
|
||||
buffer = appendRate(buffer, rate)
|
||||
buffer = appendTags(buffer, globalTags, tags)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendIntegerMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value int64, tags []string, rate float64) []byte {
|
||||
buffer = appendHeader(buffer, namespace, name)
|
||||
buffer = strconv.AppendInt(buffer, value, 10)
|
||||
buffer = append(buffer, '|')
|
||||
buffer = append(buffer, typeSymbol...)
|
||||
buffer = appendRate(buffer, rate)
|
||||
buffer = appendTags(buffer, globalTags, tags)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendStringMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value string, tags []string, rate float64) []byte {
|
||||
buffer = appendHeader(buffer, namespace, name)
|
||||
buffer = append(buffer, value...)
|
||||
buffer = append(buffer, '|')
|
||||
buffer = append(buffer, typeSymbol...)
|
||||
buffer = appendRate(buffer, rate)
|
||||
buffer = appendTags(buffer, globalTags, tags)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendGauge(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte {
|
||||
return appendFloatMetric(buffer, gaugeSymbol, namespace, globalTags, name, value, tags, rate, -1)
|
||||
}
|
||||
|
||||
func appendCount(buffer []byte, namespace string, globalTags []string, name string, value int64, tags []string, rate float64) []byte {
|
||||
return appendIntegerMetric(buffer, countSymbol, namespace, globalTags, name, value, tags, rate)
|
||||
}
|
||||
|
||||
func appendHistogram(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte {
|
||||
return appendFloatMetric(buffer, histogramSymbol, namespace, globalTags, name, value, tags, rate, -1)
|
||||
}
|
||||
|
||||
func appendDistribution(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte {
|
||||
return appendFloatMetric(buffer, distributionSymbol, namespace, globalTags, name, value, tags, rate, -1)
|
||||
}
|
||||
|
||||
func appendSet(buffer []byte, namespace string, globalTags []string, name string, value string, tags []string, rate float64) []byte {
|
||||
return appendStringMetric(buffer, setSymbol, namespace, globalTags, name, value, tags, rate)
|
||||
}
|
||||
|
||||
func appendTiming(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte {
|
||||
return appendFloatMetric(buffer, timingSymbol, namespace, globalTags, name, value, tags, rate, 6)
|
||||
}
|
||||
|
||||
func escapedEventTextLen(text string) int {
|
||||
return len(text) + strings.Count(text, "\n")
|
||||
}
|
||||
|
||||
func appendEscapedEventText(buffer []byte, text string) []byte {
|
||||
for _, b := range []byte(text) {
|
||||
if b != '\n' {
|
||||
buffer = append(buffer, b)
|
||||
} else {
|
||||
buffer = append(buffer, "\\n"...)
|
||||
}
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendEvent(buffer []byte, event Event, globalTags []string) []byte {
|
||||
escapedTextLen := escapedEventTextLen(event.Text)
|
||||
|
||||
buffer = append(buffer, "_e{"...)
|
||||
buffer = strconv.AppendInt(buffer, int64(len(event.Title)), 10)
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
buffer = strconv.AppendInt(buffer, int64(escapedTextLen), 10)
|
||||
buffer = append(buffer, "}:"...)
|
||||
buffer = append(buffer, event.Title...)
|
||||
buffer = append(buffer, '|')
|
||||
if escapedTextLen != len(event.Text) {
|
||||
buffer = appendEscapedEventText(buffer, event.Text)
|
||||
} else {
|
||||
buffer = append(buffer, event.Text...)
|
||||
}
|
||||
|
||||
if !event.Timestamp.IsZero() {
|
||||
buffer = append(buffer, "|d:"...)
|
||||
buffer = strconv.AppendInt(buffer, int64(event.Timestamp.Unix()), 10)
|
||||
}
|
||||
|
||||
if len(event.Hostname) != 0 {
|
||||
buffer = append(buffer, "|h:"...)
|
||||
buffer = append(buffer, event.Hostname...)
|
||||
}
|
||||
|
||||
if len(event.AggregationKey) != 0 {
|
||||
buffer = append(buffer, "|k:"...)
|
||||
buffer = append(buffer, event.AggregationKey...)
|
||||
}
|
||||
|
||||
if len(event.Priority) != 0 {
|
||||
buffer = append(buffer, "|p:"...)
|
||||
buffer = append(buffer, event.Priority...)
|
||||
}
|
||||
|
||||
if len(event.SourceTypeName) != 0 {
|
||||
buffer = append(buffer, "|s:"...)
|
||||
buffer = append(buffer, event.SourceTypeName...)
|
||||
}
|
||||
|
||||
if len(event.AlertType) != 0 {
|
||||
buffer = append(buffer, "|t:"...)
|
||||
buffer = append(buffer, string(event.AlertType)...)
|
||||
}
|
||||
|
||||
buffer = appendTags(buffer, globalTags, event.Tags)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendEscapedServiceCheckText(buffer []byte, text string) []byte {
|
||||
for i := 0; i < len(text); i++ {
|
||||
if text[i] == '\n' {
|
||||
buffer = append(buffer, "\\n"...)
|
||||
} else if text[i] == 'm' && i+1 < len(text) && text[i+1] == ':' {
|
||||
buffer = append(buffer, "m\\:"...)
|
||||
i++
|
||||
} else {
|
||||
buffer = append(buffer, text[i])
|
||||
}
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendServiceCheck(buffer []byte, serviceCheck ServiceCheck, globalTags []string) []byte {
|
||||
buffer = append(buffer, "_sc|"...)
|
||||
buffer = append(buffer, serviceCheck.Name...)
|
||||
buffer = append(buffer, '|')
|
||||
buffer = strconv.AppendInt(buffer, int64(serviceCheck.Status), 10)
|
||||
|
||||
if !serviceCheck.Timestamp.IsZero() {
|
||||
buffer = append(buffer, "|d:"...)
|
||||
buffer = strconv.AppendInt(buffer, int64(serviceCheck.Timestamp.Unix()), 10)
|
||||
}
|
||||
|
||||
if len(serviceCheck.Hostname) != 0 {
|
||||
buffer = append(buffer, "|h:"...)
|
||||
buffer = append(buffer, serviceCheck.Hostname...)
|
||||
}
|
||||
|
||||
buffer = appendTags(buffer, globalTags, serviceCheck.Tags)
|
||||
|
||||
if len(serviceCheck.Message) != 0 {
|
||||
buffer = append(buffer, "|m:"...)
|
||||
buffer = appendEscapedServiceCheckText(buffer, serviceCheck.Message)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendSeparator(buffer []byte) []byte {
|
||||
return append(buffer, '\n')
|
||||
}
|
||||
181
vendor/github.com/DataDog/datadog-go/statsd/metrics.go
generated
vendored
Normal file
181
vendor/github.com/DataDog/datadog-go/statsd/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
/*
|
||||
Those are metrics type that can be aggregated on the client side:
|
||||
- Gauge
|
||||
- Count
|
||||
- Set
|
||||
*/
|
||||
|
||||
type countMetric struct {
|
||||
value int64
|
||||
name string
|
||||
tags []string
|
||||
}
|
||||
|
||||
func newCountMetric(name string, value int64, tags []string) *countMetric {
|
||||
return &countMetric{
|
||||
value: value,
|
||||
name: name,
|
||||
tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *countMetric) sample(v int64) {
|
||||
atomic.AddInt64(&c.value, v)
|
||||
}
|
||||
|
||||
func (c *countMetric) flushUnsafe() metric {
|
||||
return metric{
|
||||
metricType: count,
|
||||
name: c.name,
|
||||
tags: c.tags,
|
||||
rate: 1,
|
||||
ivalue: c.value,
|
||||
}
|
||||
}
|
||||
|
||||
// Gauge
|
||||
|
||||
type gaugeMetric struct {
|
||||
value uint64
|
||||
name string
|
||||
tags []string
|
||||
}
|
||||
|
||||
func newGaugeMetric(name string, value float64, tags []string) *gaugeMetric {
|
||||
return &gaugeMetric{
|
||||
value: math.Float64bits(value),
|
||||
name: name,
|
||||
tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gaugeMetric) sample(v float64) {
|
||||
atomic.StoreUint64(&g.value, math.Float64bits(v))
|
||||
}
|
||||
|
||||
func (g *gaugeMetric) flushUnsafe() metric {
|
||||
return metric{
|
||||
metricType: gauge,
|
||||
name: g.name,
|
||||
tags: g.tags,
|
||||
rate: 1,
|
||||
fvalue: math.Float64frombits(g.value),
|
||||
}
|
||||
}
|
||||
|
||||
// Set
|
||||
|
||||
type setMetric struct {
|
||||
data map[string]struct{}
|
||||
name string
|
||||
tags []string
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func newSetMetric(name string, value string, tags []string) *setMetric {
|
||||
set := &setMetric{
|
||||
data: map[string]struct{}{},
|
||||
name: name,
|
||||
tags: tags,
|
||||
}
|
||||
set.data[value] = struct{}{}
|
||||
return set
|
||||
}
|
||||
|
||||
func (s *setMetric) sample(v string) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.data[v] = struct{}{}
|
||||
}
|
||||
|
||||
// Sets are aggregated on the agent side too. We flush the keys so a set from
|
||||
// multiple application can be correctly aggregated on the agent side.
|
||||
func (s *setMetric) flushUnsafe() []metric {
|
||||
if len(s.data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
metrics := make([]metric, len(s.data))
|
||||
i := 0
|
||||
for value := range s.data {
|
||||
metrics[i] = metric{
|
||||
metricType: set,
|
||||
name: s.name,
|
||||
tags: s.tags,
|
||||
rate: 1,
|
||||
svalue: value,
|
||||
}
|
||||
i++
|
||||
}
|
||||
return metrics
|
||||
}
|
||||
|
||||
// Histograms, Distributions and Timings
|
||||
|
||||
type bufferedMetric struct {
|
||||
sync.Mutex
|
||||
|
||||
data []float64
|
||||
name string
|
||||
// Histograms and Distributions store tags as one string since we need
|
||||
// to compute its size multiple time when serializing.
|
||||
tags string
|
||||
mtype metricType
|
||||
}
|
||||
|
||||
func (s *bufferedMetric) sample(v float64) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.data = append(s.data, v)
|
||||
}
|
||||
|
||||
func (s *bufferedMetric) flushUnsafe() metric {
|
||||
return metric{
|
||||
metricType: s.mtype,
|
||||
name: s.name,
|
||||
stags: s.tags,
|
||||
rate: 1,
|
||||
fvalues: s.data,
|
||||
}
|
||||
}
|
||||
|
||||
type histogramMetric = bufferedMetric
|
||||
|
||||
func newHistogramMetric(name string, value float64, stringTags string) *histogramMetric {
|
||||
return &histogramMetric{
|
||||
data: []float64{value},
|
||||
name: name,
|
||||
tags: stringTags,
|
||||
mtype: histogramAggregated,
|
||||
}
|
||||
}
|
||||
|
||||
type distributionMetric = bufferedMetric
|
||||
|
||||
func newDistributionMetric(name string, value float64, stringTags string) *distributionMetric {
|
||||
return &distributionMetric{
|
||||
data: []float64{value},
|
||||
name: name,
|
||||
tags: stringTags,
|
||||
mtype: distributionAggregated,
|
||||
}
|
||||
}
|
||||
|
||||
type timingMetric = bufferedMetric
|
||||
|
||||
func newTimingMetric(name string, value float64, stringTags string) *timingMetric {
|
||||
return &timingMetric{
|
||||
data: []float64{value},
|
||||
name: name,
|
||||
tags: stringTags,
|
||||
mtype: timingAggregated,
|
||||
}
|
||||
}
|
||||
91
vendor/github.com/DataDog/datadog-go/statsd/noop.go
generated
vendored
Normal file
91
vendor/github.com/DataDog/datadog-go/statsd/noop.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package statsd
|
||||
|
||||
import "time"
|
||||
|
||||
// NoOpClient is a statsd client that does nothing. Can be useful in testing
|
||||
// situations for library users.
|
||||
type NoOpClient struct{}
|
||||
|
||||
// Gauge does nothing and returns nil
|
||||
func (n *NoOpClient) Gauge(name string, value float64, tags []string, rate float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Count does nothing and returns nil
|
||||
func (n *NoOpClient) Count(name string, value int64, tags []string, rate float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Histogram does nothing and returns nil
|
||||
func (n *NoOpClient) Histogram(name string, value float64, tags []string, rate float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Distribution does nothing and returns nil
|
||||
func (n *NoOpClient) Distribution(name string, value float64, tags []string, rate float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decr does nothing and returns nil
|
||||
func (n *NoOpClient) Decr(name string, tags []string, rate float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Incr does nothing and returns nil
|
||||
func (n *NoOpClient) Incr(name string, tags []string, rate float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set does nothing and returns nil
|
||||
func (n *NoOpClient) Set(name string, value string, tags []string, rate float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Timing does nothing and returns nil
|
||||
func (n *NoOpClient) Timing(name string, value time.Duration, tags []string, rate float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TimeInMilliseconds does nothing and returns nil
|
||||
func (n *NoOpClient) TimeInMilliseconds(name string, value float64, tags []string, rate float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Event does nothing and returns nil
|
||||
func (n *NoOpClient) Event(e *Event) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SimpleEvent does nothing and returns nil
|
||||
func (n *NoOpClient) SimpleEvent(title, text string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServiceCheck does nothing and returns nil
|
||||
func (n *NoOpClient) ServiceCheck(sc *ServiceCheck) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SimpleServiceCheck does nothing and returns nil
|
||||
func (n *NoOpClient) SimpleServiceCheck(name string, status ServiceCheckStatus) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close does nothing and returns nil
|
||||
func (n *NoOpClient) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush does nothing and returns nil
|
||||
func (n *NoOpClient) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteTimeout does nothing and returns nil
|
||||
func (n *NoOpClient) SetWriteTimeout(d time.Duration) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify that NoOpClient implements the ClientInterface.
|
||||
// https://golang.org/doc/faq#guarantee_satisfies_interface
|
||||
var _ ClientInterface = &NoOpClient{}
|
||||
323
vendor/github.com/DataDog/datadog-go/statsd/options.go
generated
vendored
Normal file
323
vendor/github.com/DataDog/datadog-go/statsd/options.go
generated
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultNamespace is the default value for the Namespace option
|
||||
DefaultNamespace = ""
|
||||
// DefaultTags is the default value for the Tags option
|
||||
DefaultTags = []string{}
|
||||
// DefaultMaxBytesPerPayload is the default value for the MaxBytesPerPayload option
|
||||
DefaultMaxBytesPerPayload = 0
|
||||
// DefaultMaxMessagesPerPayload is the default value for the MaxMessagesPerPayload option
|
||||
DefaultMaxMessagesPerPayload = math.MaxInt32
|
||||
// DefaultBufferPoolSize is the default value for the DefaultBufferPoolSize option
|
||||
DefaultBufferPoolSize = 0
|
||||
// DefaultBufferFlushInterval is the default value for the BufferFlushInterval option
|
||||
DefaultBufferFlushInterval = 100 * time.Millisecond
|
||||
// DefaultBufferShardCount is the default value for the BufferShardCount option
|
||||
DefaultBufferShardCount = 32
|
||||
// DefaultSenderQueueSize is the default value for the DefaultSenderQueueSize option
|
||||
DefaultSenderQueueSize = 0
|
||||
// DefaultWriteTimeoutUDS is the default value for the WriteTimeoutUDS option
|
||||
DefaultWriteTimeoutUDS = 100 * time.Millisecond
|
||||
// DefaultTelemetry is the default value for the Telemetry option
|
||||
DefaultTelemetry = true
|
||||
// DefaultReceivingMode is the default behavior when sending metrics
|
||||
DefaultReceivingMode = MutexMode
|
||||
// DefaultChannelModeBufferSize is the default size of the channel holding incoming metrics
|
||||
DefaultChannelModeBufferSize = 4096
|
||||
// DefaultAggregationFlushInterval is the default interval for the aggregator to flush metrics.
|
||||
// This should divide the Agent reporting period (default=10s) evenly to reduce "aliasing" that
|
||||
// can cause values to appear irregular.
|
||||
DefaultAggregationFlushInterval = 2 * time.Second
|
||||
// DefaultAggregation
|
||||
DefaultAggregation = false
|
||||
// DefaultExtendedAggregation
|
||||
DefaultExtendedAggregation = false
|
||||
// DefaultDevMode
|
||||
DefaultDevMode = false
|
||||
)
|
||||
|
||||
// Options contains the configuration options for a client.
|
||||
type Options struct {
|
||||
// Namespace to prepend to all metrics, events and service checks name.
|
||||
Namespace string
|
||||
// Tags are global tags to be applied to every metrics, events and service checks.
|
||||
Tags []string
|
||||
// MaxBytesPerPayload is the maximum number of bytes a single payload will contain.
|
||||
// The magic value 0 will set the option to the optimal size for the transport
|
||||
// protocol used when creating the client: 1432 for UDP and 8192 for UDS.
|
||||
MaxBytesPerPayload int
|
||||
// MaxMessagesPerPayload is the maximum number of metrics, events and/or service checks a single payload will contain.
|
||||
// This option can be set to `1` to create an unbuffered client.
|
||||
MaxMessagesPerPayload int
|
||||
// BufferPoolSize is the size of the pool of buffers in number of buffers.
|
||||
// The magic value 0 will set the option to the optimal size for the transport
|
||||
// protocol used when creating the client: 2048 for UDP and 512 for UDS.
|
||||
BufferPoolSize int
|
||||
// BufferFlushInterval is the interval after which the current buffer will get flushed.
|
||||
BufferFlushInterval time.Duration
|
||||
// BufferShardCount is the number of buffer "shards" that will be used.
|
||||
// Those shards allows the use of multiple buffers at the same time to reduce
|
||||
// lock contention.
|
||||
BufferShardCount int
|
||||
// SenderQueueSize is the size of the sender queue in number of buffers.
|
||||
// The magic value 0 will set the option to the optimal size for the transport
|
||||
// protocol used when creating the client: 2048 for UDP and 512 for UDS.
|
||||
SenderQueueSize int
|
||||
// WriteTimeoutUDS is the timeout after which a UDS packet is dropped.
|
||||
WriteTimeoutUDS time.Duration
|
||||
// Telemetry is a set of metrics automatically injected by the client in the
|
||||
// dogstatsd stream to be able to monitor the client itself.
|
||||
Telemetry bool
|
||||
// ReceiveMode determins the behavior of the client when receiving to many
|
||||
// metrics. The client will either drop the metrics if its buffers are
|
||||
// full (ChannelMode mode) or block the caller until the metric can be
|
||||
// handled (MutexMode mode). By default the client will MutexMode. This
|
||||
// option should be set to ChannelMode only when use under very high
|
||||
// load.
|
||||
//
|
||||
// MutexMode uses a mutex internally which is much faster than
|
||||
// channel but causes some lock contention when used with a high number
|
||||
// of threads. Mutex are sharded based on the metrics name which
|
||||
// limit mutex contention when goroutines send different metrics.
|
||||
//
|
||||
// ChannelMode: uses channel (of ChannelModeBufferSize size) to send
|
||||
// metrics and drop metrics if the channel is full. Sending metrics in
|
||||
// this mode is slower that MutexMode (because of the channel), but
|
||||
// will not block the application. This mode is made for application
|
||||
// using many goroutines, sending the same metrics at a very high
|
||||
// volume. The goal is to not slow down the application at the cost of
|
||||
// dropping metrics and having a lower max throughput.
|
||||
ReceiveMode ReceivingMode
|
||||
// ChannelModeBufferSize is the size of the channel holding incoming metrics
|
||||
ChannelModeBufferSize int
|
||||
// AggregationFlushInterval is the interval for the aggregator to flush metrics
|
||||
AggregationFlushInterval time.Duration
|
||||
// [beta] Aggregation enables/disables client side aggregation for
|
||||
// Gauges, Counts and Sets (compatible with every Agent's version).
|
||||
Aggregation bool
|
||||
// [beta] Extended aggregation enables/disables client side aggregation
|
||||
// for all types. This feature is only compatible with Agent's versions
|
||||
// >=7.25.0 or Agent's version >=6.25.0 && < 7.0.0.
|
||||
ExtendedAggregation bool
|
||||
// TelemetryAddr specify a different endpoint for telemetry metrics.
|
||||
TelemetryAddr string
|
||||
// DevMode enables the "dev" mode where the client sends much more
|
||||
// telemetry metrics to help troubleshooting the client behavior.
|
||||
DevMode bool
|
||||
}
|
||||
|
||||
func resolveOptions(options []Option) (*Options, error) {
|
||||
o := &Options{
|
||||
Namespace: DefaultNamespace,
|
||||
Tags: DefaultTags,
|
||||
MaxBytesPerPayload: DefaultMaxBytesPerPayload,
|
||||
MaxMessagesPerPayload: DefaultMaxMessagesPerPayload,
|
||||
BufferPoolSize: DefaultBufferPoolSize,
|
||||
BufferFlushInterval: DefaultBufferFlushInterval,
|
||||
BufferShardCount: DefaultBufferShardCount,
|
||||
SenderQueueSize: DefaultSenderQueueSize,
|
||||
WriteTimeoutUDS: DefaultWriteTimeoutUDS,
|
||||
Telemetry: DefaultTelemetry,
|
||||
ReceiveMode: DefaultReceivingMode,
|
||||
ChannelModeBufferSize: DefaultChannelModeBufferSize,
|
||||
AggregationFlushInterval: DefaultAggregationFlushInterval,
|
||||
Aggregation: DefaultAggregation,
|
||||
ExtendedAggregation: DefaultExtendedAggregation,
|
||||
DevMode: DefaultDevMode,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
err := option(o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// Option is a client option. Can return an error if validation fails.
|
||||
type Option func(*Options) error
|
||||
|
||||
// WithNamespace sets the Namespace option.
|
||||
func WithNamespace(namespace string) Option {
|
||||
return func(o *Options) error {
|
||||
if strings.HasSuffix(namespace, ".") {
|
||||
o.Namespace = namespace
|
||||
} else {
|
||||
o.Namespace = namespace + "."
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithTags sets the Tags option.
|
||||
func WithTags(tags []string) Option {
|
||||
return func(o *Options) error {
|
||||
o.Tags = tags
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxMessagesPerPayload sets the MaxMessagesPerPayload option.
|
||||
func WithMaxMessagesPerPayload(maxMessagesPerPayload int) Option {
|
||||
return func(o *Options) error {
|
||||
o.MaxMessagesPerPayload = maxMessagesPerPayload
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxBytesPerPayload sets the MaxBytesPerPayload option.
|
||||
func WithMaxBytesPerPayload(MaxBytesPerPayload int) Option {
|
||||
return func(o *Options) error {
|
||||
o.MaxBytesPerPayload = MaxBytesPerPayload
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithBufferPoolSize sets the BufferPoolSize option.
|
||||
func WithBufferPoolSize(bufferPoolSize int) Option {
|
||||
return func(o *Options) error {
|
||||
o.BufferPoolSize = bufferPoolSize
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithBufferFlushInterval sets the BufferFlushInterval option.
|
||||
func WithBufferFlushInterval(bufferFlushInterval time.Duration) Option {
|
||||
return func(o *Options) error {
|
||||
o.BufferFlushInterval = bufferFlushInterval
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithBufferShardCount sets the BufferShardCount option.
|
||||
func WithBufferShardCount(bufferShardCount int) Option {
|
||||
return func(o *Options) error {
|
||||
if bufferShardCount < 1 {
|
||||
return fmt.Errorf("BufferShardCount must be a positive integer")
|
||||
}
|
||||
o.BufferShardCount = bufferShardCount
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithSenderQueueSize sets the SenderQueueSize option.
|
||||
func WithSenderQueueSize(senderQueueSize int) Option {
|
||||
return func(o *Options) error {
|
||||
o.SenderQueueSize = senderQueueSize
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithWriteTimeoutUDS sets the WriteTimeoutUDS option.
|
||||
func WithWriteTimeoutUDS(writeTimeoutUDS time.Duration) Option {
|
||||
return func(o *Options) error {
|
||||
o.WriteTimeoutUDS = writeTimeoutUDS
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutTelemetry disables the telemetry
|
||||
func WithoutTelemetry() Option {
|
||||
return func(o *Options) error {
|
||||
o.Telemetry = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithChannelMode will use channel to receive metrics
|
||||
func WithChannelMode() Option {
|
||||
return func(o *Options) error {
|
||||
o.ReceiveMode = ChannelMode
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMutexMode will use mutex to receive metrics
|
||||
func WithMutexMode() Option {
|
||||
return func(o *Options) error {
|
||||
o.ReceiveMode = MutexMode
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithChannelModeBufferSize the channel buffer size when using "drop mode"
|
||||
func WithChannelModeBufferSize(bufferSize int) Option {
|
||||
return func(o *Options) error {
|
||||
o.ChannelModeBufferSize = bufferSize
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithAggregationInterval set the aggregation interval
|
||||
func WithAggregationInterval(interval time.Duration) Option {
|
||||
return func(o *Options) error {
|
||||
o.AggregationFlushInterval = interval
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithClientSideAggregation enables client side aggregation for Gauges, Counts
|
||||
// and Sets. Client side aggregation is a beta feature.
|
||||
func WithClientSideAggregation() Option {
|
||||
return func(o *Options) error {
|
||||
o.Aggregation = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutClientSideAggregation disables client side aggregation.
|
||||
func WithoutClientSideAggregation() Option {
|
||||
return func(o *Options) error {
|
||||
o.Aggregation = false
|
||||
o.ExtendedAggregation = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithExtendedClientSideAggregation enables client side aggregation for all
|
||||
// types. This feature is only compatible with Agent's version >=6.25.0 &&
|
||||
// <7.0.0 or Agent's versions >=7.25.0. Client side aggregation is a beta
|
||||
// feature.
|
||||
func WithExtendedClientSideAggregation() Option {
|
||||
return func(o *Options) error {
|
||||
o.Aggregation = true
|
||||
o.ExtendedAggregation = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithTelemetryAddr specify a different address for telemetry metrics.
|
||||
func WithTelemetryAddr(addr string) Option {
|
||||
return func(o *Options) error {
|
||||
o.TelemetryAddr = addr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDevMode enables client "dev" mode, sending more Telemetry metrics to
|
||||
// help troubleshoot client behavior.
|
||||
func WithDevMode() Option {
|
||||
return func(o *Options) error {
|
||||
o.DevMode = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutDevMode disables client "dev" mode, sending more Telemetry metrics to
|
||||
// help troubleshoot client behavior.
|
||||
func WithoutDevMode() Option {
|
||||
return func(o *Options) error {
|
||||
o.DevMode = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
9
vendor/github.com/DataDog/datadog-go/statsd/pipe.go
generated
vendored
Normal file
9
vendor/github.com/DataDog/datadog-go/statsd/pipe.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// +build !windows
|
||||
|
||||
package statsd
|
||||
|
||||
import "errors"
|
||||
|
||||
func newWindowsPipeWriter(pipepath string) (statsdWriter, error) {
|
||||
return nil, errors.New("Windows Named Pipes are only supported on Windows")
|
||||
}
|
||||
84
vendor/github.com/DataDog/datadog-go/statsd/pipe_windows.go
generated
vendored
Normal file
84
vendor/github.com/DataDog/datadog-go/statsd/pipe_windows.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// +build windows
|
||||
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
)
|
||||
|
||||
const defaultPipeTimeout = 1 * time.Millisecond
|
||||
|
||||
type pipeWriter struct {
|
||||
mu sync.RWMutex
|
||||
conn net.Conn
|
||||
timeout time.Duration
|
||||
pipepath string
|
||||
}
|
||||
|
||||
func (p *pipeWriter) SetWriteTimeout(d time.Duration) error {
|
||||
p.mu.Lock()
|
||||
p.timeout = d
|
||||
p.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pipeWriter) Write(data []byte) (n int, err error) {
|
||||
conn, err := p.ensureConnection()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
p.mu.RLock()
|
||||
conn.SetWriteDeadline(time.Now().Add(p.timeout))
|
||||
p.mu.RUnlock()
|
||||
|
||||
n, err = conn.Write(data)
|
||||
if err != nil {
|
||||
if e, ok := err.(net.Error); !ok || !e.Temporary() {
|
||||
// disconnected; retry again on next attempt
|
||||
p.mu.Lock()
|
||||
p.conn = nil
|
||||
p.mu.Unlock()
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (p *pipeWriter) ensureConnection() (net.Conn, error) {
|
||||
p.mu.RLock()
|
||||
conn := p.conn
|
||||
p.mu.RUnlock()
|
||||
if conn != nil {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// looks like we might need to connect - try again with write locking.
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if p.conn != nil {
|
||||
return p.conn, nil
|
||||
}
|
||||
newconn, err := winio.DialPipe(p.pipepath, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.conn = newconn
|
||||
return newconn, nil
|
||||
}
|
||||
|
||||
func (p *pipeWriter) Close() error {
|
||||
return p.conn.Close()
|
||||
}
|
||||
|
||||
func newWindowsPipeWriter(pipepath string) (*pipeWriter, error) {
|
||||
// Defer connection establishment to first write
|
||||
return &pipeWriter{
|
||||
conn: nil,
|
||||
timeout: defaultPipeTimeout,
|
||||
pipepath: pipepath,
|
||||
}, nil
|
||||
}
|
||||
130
vendor/github.com/DataDog/datadog-go/statsd/sender.go
generated
vendored
Normal file
130
vendor/github.com/DataDog/datadog-go/statsd/sender.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A statsdWriter offers a standard interface regardless of the underlying
|
||||
// protocol. For now UDS and UPD writers are available.
|
||||
// Attention: the underlying buffer of `data` is reused after a `statsdWriter.Write` call.
|
||||
// `statsdWriter.Write` must be synchronous.
|
||||
type statsdWriter interface {
|
||||
Write(data []byte) (n int, err error)
|
||||
SetWriteTimeout(time.Duration) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
// SenderMetrics contains metrics about the health of the sender
|
||||
type SenderMetrics struct {
|
||||
TotalSentBytes uint64
|
||||
TotalSentPayloads uint64
|
||||
TotalDroppedPayloads uint64
|
||||
TotalDroppedBytes uint64
|
||||
TotalDroppedPayloadsQueueFull uint64
|
||||
TotalDroppedBytesQueueFull uint64
|
||||
TotalDroppedPayloadsWriter uint64
|
||||
TotalDroppedBytesWriter uint64
|
||||
}
|
||||
|
||||
type sender struct {
|
||||
transport statsdWriter
|
||||
pool *bufferPool
|
||||
queue chan *statsdBuffer
|
||||
metrics *SenderMetrics
|
||||
stop chan struct{}
|
||||
flushSignal chan struct{}
|
||||
}
|
||||
|
||||
func newSender(transport statsdWriter, queueSize int, pool *bufferPool) *sender {
|
||||
sender := &sender{
|
||||
transport: transport,
|
||||
pool: pool,
|
||||
queue: make(chan *statsdBuffer, queueSize),
|
||||
metrics: &SenderMetrics{},
|
||||
stop: make(chan struct{}),
|
||||
flushSignal: make(chan struct{}),
|
||||
}
|
||||
|
||||
go sender.sendLoop()
|
||||
return sender
|
||||
}
|
||||
|
||||
func (s *sender) send(buffer *statsdBuffer) {
|
||||
select {
|
||||
case s.queue <- buffer:
|
||||
default:
|
||||
atomic.AddUint64(&s.metrics.TotalDroppedPayloads, 1)
|
||||
atomic.AddUint64(&s.metrics.TotalDroppedBytes, uint64(len(buffer.bytes())))
|
||||
atomic.AddUint64(&s.metrics.TotalDroppedPayloadsQueueFull, 1)
|
||||
atomic.AddUint64(&s.metrics.TotalDroppedBytesQueueFull, uint64(len(buffer.bytes())))
|
||||
s.pool.returnBuffer(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sender) write(buffer *statsdBuffer) {
|
||||
_, err := s.transport.Write(buffer.bytes())
|
||||
if err != nil {
|
||||
atomic.AddUint64(&s.metrics.TotalDroppedPayloads, 1)
|
||||
atomic.AddUint64(&s.metrics.TotalDroppedBytes, uint64(len(buffer.bytes())))
|
||||
atomic.AddUint64(&s.metrics.TotalDroppedPayloadsWriter, 1)
|
||||
atomic.AddUint64(&s.metrics.TotalDroppedBytesWriter, uint64(len(buffer.bytes())))
|
||||
} else {
|
||||
atomic.AddUint64(&s.metrics.TotalSentPayloads, 1)
|
||||
atomic.AddUint64(&s.metrics.TotalSentBytes, uint64(len(buffer.bytes())))
|
||||
}
|
||||
s.pool.returnBuffer(buffer)
|
||||
}
|
||||
|
||||
func (s *sender) flushTelemetryMetrics() SenderMetrics {
|
||||
return SenderMetrics{
|
||||
TotalSentBytes: atomic.SwapUint64(&s.metrics.TotalSentBytes, 0),
|
||||
TotalSentPayloads: atomic.SwapUint64(&s.metrics.TotalSentPayloads, 0),
|
||||
TotalDroppedPayloads: atomic.SwapUint64(&s.metrics.TotalDroppedPayloads, 0),
|
||||
TotalDroppedBytes: atomic.SwapUint64(&s.metrics.TotalDroppedBytes, 0),
|
||||
TotalDroppedPayloadsQueueFull: atomic.SwapUint64(&s.metrics.TotalDroppedPayloadsQueueFull, 0),
|
||||
TotalDroppedBytesQueueFull: atomic.SwapUint64(&s.metrics.TotalDroppedBytesQueueFull, 0),
|
||||
TotalDroppedPayloadsWriter: atomic.SwapUint64(&s.metrics.TotalDroppedPayloadsWriter, 0),
|
||||
TotalDroppedBytesWriter: atomic.SwapUint64(&s.metrics.TotalDroppedBytesWriter, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sender) sendLoop() {
|
||||
defer close(s.stop)
|
||||
for {
|
||||
select {
|
||||
case buffer := <-s.queue:
|
||||
s.write(buffer)
|
||||
case <-s.stop:
|
||||
return
|
||||
case <-s.flushSignal:
|
||||
// At that point we know that the workers are paused (the statsd client
|
||||
// will pause them before calling sender.flush()).
|
||||
// So we can fully flush the input queue
|
||||
s.flushInputQueue()
|
||||
s.flushSignal <- struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sender) flushInputQueue() {
|
||||
for {
|
||||
select {
|
||||
case buffer := <-s.queue:
|
||||
s.write(buffer)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
func (s *sender) flush() {
|
||||
s.flushSignal <- struct{}{}
|
||||
<-s.flushSignal
|
||||
}
|
||||
|
||||
func (s *sender) close() error {
|
||||
s.stop <- struct{}{}
|
||||
<-s.stop
|
||||
s.flushInputQueue()
|
||||
return s.transport.Close()
|
||||
}
|
||||
70
vendor/github.com/DataDog/datadog-go/statsd/service_check.go
generated
vendored
Normal file
70
vendor/github.com/DataDog/datadog-go/statsd/service_check.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ServiceCheckStatus support
|
||||
type ServiceCheckStatus byte
|
||||
|
||||
const (
|
||||
// Ok is the "ok" ServiceCheck status
|
||||
Ok ServiceCheckStatus = 0
|
||||
// Warn is the "warning" ServiceCheck status
|
||||
Warn ServiceCheckStatus = 1
|
||||
// Critical is the "critical" ServiceCheck status
|
||||
Critical ServiceCheckStatus = 2
|
||||
// Unknown is the "unknown" ServiceCheck status
|
||||
Unknown ServiceCheckStatus = 3
|
||||
)
|
||||
|
||||
// A ServiceCheck is an object that contains status of DataDog service check.
|
||||
type ServiceCheck struct {
|
||||
// Name of the service check. Required.
|
||||
Name string
|
||||
// Status of service check. Required.
|
||||
Status ServiceCheckStatus
|
||||
// Timestamp is a timestamp for the serviceCheck. If not provided, the dogstatsd
|
||||
// server will set this to the current time.
|
||||
Timestamp time.Time
|
||||
// Hostname for the serviceCheck.
|
||||
Hostname string
|
||||
// A message describing the current state of the serviceCheck.
|
||||
Message string
|
||||
// Tags for the serviceCheck.
|
||||
Tags []string
|
||||
}
|
||||
|
||||
// NewServiceCheck creates a new serviceCheck with the given name and status. Error checking
|
||||
// against these values is done at send-time, or upon running sc.Check.
|
||||
func NewServiceCheck(name string, status ServiceCheckStatus) *ServiceCheck {
|
||||
return &ServiceCheck{
|
||||
Name: name,
|
||||
Status: status,
|
||||
}
|
||||
}
|
||||
|
||||
// Check verifies that a service check is valid.
|
||||
func (sc ServiceCheck) Check() error {
|
||||
if len(sc.Name) == 0 {
|
||||
return fmt.Errorf("statsd.ServiceCheck name is required")
|
||||
}
|
||||
if byte(sc.Status) < 0 || byte(sc.Status) > 3 {
|
||||
return fmt.Errorf("statsd.ServiceCheck status has invalid value")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode returns the dogstatsd wire protocol representation for a service check.
|
||||
// Tags may be passed which will be added to the encoded output but not to
|
||||
// the Service Check's list of tags, eg. for default tags.
|
||||
func (sc ServiceCheck) Encode(tags ...string) (string, error) {
|
||||
err := sc.Check()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var buffer []byte
|
||||
buffer = appendServiceCheck(buffer, sc, tags)
|
||||
return string(buffer), nil
|
||||
}
|
||||
687
vendor/github.com/DataDog/datadog-go/statsd/statsd.go
generated
vendored
Normal file
687
vendor/github.com/DataDog/datadog-go/statsd/statsd.go
generated
vendored
Normal file
@@ -0,0 +1,687 @@
|
||||
// Copyright 2013 Ooyala, Inc.
|
||||
|
||||
/*
|
||||
Package statsd provides a Go dogstatsd client. Dogstatsd extends the popular statsd,
|
||||
adding tags and histograms and pushing upstream to Datadog.
|
||||
|
||||
Refer to http://docs.datadoghq.com/guides/dogstatsd/ for information about DogStatsD.
|
||||
|
||||
statsd is based on go-statsd-client.
|
||||
*/
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
OptimalUDPPayloadSize defines the optimal payload size for a UDP datagram, 1432 bytes
|
||||
is optimal for regular networks with an MTU of 1500 so datagrams don't get
|
||||
fragmented. It's generally recommended not to fragment UDP datagrams as losing
|
||||
a single fragment will cause the entire datagram to be lost.
|
||||
*/
|
||||
const OptimalUDPPayloadSize = 1432
|
||||
|
||||
/*
|
||||
MaxUDPPayloadSize defines the maximum payload size for a UDP datagram.
|
||||
Its value comes from the calculation: 65535 bytes Max UDP datagram size -
|
||||
8byte UDP header - 60byte max IP headers
|
||||
any number greater than that will see frames being cut out.
|
||||
*/
|
||||
const MaxUDPPayloadSize = 65467
|
||||
|
||||
// DefaultUDPBufferPoolSize is the default size of the buffer pool for UDP clients.
|
||||
const DefaultUDPBufferPoolSize = 2048
|
||||
|
||||
// DefaultUDSBufferPoolSize is the default size of the buffer pool for UDS clients.
|
||||
const DefaultUDSBufferPoolSize = 512
|
||||
|
||||
/*
|
||||
DefaultMaxAgentPayloadSize is the default maximum payload size the agent
|
||||
can receive. This can be adjusted by changing dogstatsd_buffer_size in the
|
||||
agent configuration file datadog.yaml. This is also used as the optimal payload size
|
||||
for UDS datagrams.
|
||||
*/
|
||||
const DefaultMaxAgentPayloadSize = 8192
|
||||
|
||||
/*
|
||||
UnixAddressPrefix holds the prefix to use to enable Unix Domain Socket
|
||||
traffic instead of UDP.
|
||||
*/
|
||||
const UnixAddressPrefix = "unix://"
|
||||
|
||||
/*
|
||||
WindowsPipeAddressPrefix holds the prefix to use to enable Windows Named Pipes
|
||||
traffic instead of UDP.
|
||||
*/
|
||||
const WindowsPipeAddressPrefix = `\\.\pipe\`
|
||||
|
||||
const (
|
||||
agentHostEnvVarName = "DD_AGENT_HOST"
|
||||
agentPortEnvVarName = "DD_DOGSTATSD_PORT"
|
||||
defaultUDPPort = "8125"
|
||||
)
|
||||
|
||||
/*
|
||||
ddEnvTagsMapping is a mapping of each "DD_" prefixed environment variable
|
||||
to a specific tag name. We use a slice to keep the order and simplify tests.
|
||||
*/
|
||||
var ddEnvTagsMapping = []struct{ envName, tagName string }{
|
||||
{"DD_ENTITY_ID", "dd.internal.entity_id"}, // Client-side entity ID injection for container tagging.
|
||||
{"DD_ENV", "env"}, // The name of the env in which the service runs.
|
||||
{"DD_SERVICE", "service"}, // The name of the running service.
|
||||
{"DD_VERSION", "version"}, // The current version of the running service.
|
||||
}
|
||||
|
||||
type metricType int
|
||||
|
||||
const (
|
||||
gauge metricType = iota
|
||||
count
|
||||
histogram
|
||||
histogramAggregated
|
||||
distribution
|
||||
distributionAggregated
|
||||
set
|
||||
timing
|
||||
timingAggregated
|
||||
event
|
||||
serviceCheck
|
||||
)
|
||||
|
||||
type ReceivingMode int
|
||||
|
||||
const (
|
||||
MutexMode ReceivingMode = iota
|
||||
ChannelMode
|
||||
)
|
||||
|
||||
const (
|
||||
WriterNameUDP string = "udp"
|
||||
WriterNameUDS string = "uds"
|
||||
WriterWindowsPipe string = "pipe"
|
||||
)
|
||||
|
||||
type metric struct {
|
||||
metricType metricType
|
||||
namespace string
|
||||
globalTags []string
|
||||
name string
|
||||
fvalue float64
|
||||
fvalues []float64
|
||||
ivalue int64
|
||||
svalue string
|
||||
evalue *Event
|
||||
scvalue *ServiceCheck
|
||||
tags []string
|
||||
stags string
|
||||
rate float64
|
||||
}
|
||||
|
||||
type noClientErr string
|
||||
|
||||
// ErrNoClient is returned if statsd reporting methods are invoked on
|
||||
// a nil client.
|
||||
const ErrNoClient = noClientErr("statsd client is nil")
|
||||
|
||||
func (e noClientErr) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// ClientInterface is an interface that exposes the common client functions for the
|
||||
// purpose of being able to provide a no-op client or even mocking. This can aid
|
||||
// downstream users' with their testing.
|
||||
type ClientInterface interface {
|
||||
// Gauge measures the value of a metric at a particular time.
|
||||
Gauge(name string, value float64, tags []string, rate float64) error
|
||||
|
||||
// Count tracks how many times something happened per second.
|
||||
Count(name string, value int64, tags []string, rate float64) error
|
||||
|
||||
// Histogram tracks the statistical distribution of a set of values on each host.
|
||||
Histogram(name string, value float64, tags []string, rate float64) error
|
||||
|
||||
// Distribution tracks the statistical distribution of a set of values across your infrastructure.
|
||||
Distribution(name string, value float64, tags []string, rate float64) error
|
||||
|
||||
// Decr is just Count of -1
|
||||
Decr(name string, tags []string, rate float64) error
|
||||
|
||||
// Incr is just Count of 1
|
||||
Incr(name string, tags []string, rate float64) error
|
||||
|
||||
// Set counts the number of unique elements in a group.
|
||||
Set(name string, value string, tags []string, rate float64) error
|
||||
|
||||
// Timing sends timing information, it is an alias for TimeInMilliseconds
|
||||
Timing(name string, value time.Duration, tags []string, rate float64) error
|
||||
|
||||
// TimeInMilliseconds sends timing information in milliseconds.
|
||||
// It is flushed by statsd with percentiles, mean and other info (https://github.com/etsy/statsd/blob/master/docs/metric_types.md#timing)
|
||||
TimeInMilliseconds(name string, value float64, tags []string, rate float64) error
|
||||
|
||||
// Event sends the provided Event.
|
||||
Event(e *Event) error
|
||||
|
||||
// SimpleEvent sends an event with the provided title and text.
|
||||
SimpleEvent(title, text string) error
|
||||
|
||||
// ServiceCheck sends the provided ServiceCheck.
|
||||
ServiceCheck(sc *ServiceCheck) error
|
||||
|
||||
// SimpleServiceCheck sends an serviceCheck with the provided name and status.
|
||||
SimpleServiceCheck(name string, status ServiceCheckStatus) error
|
||||
|
||||
// Close the client connection.
|
||||
Close() error
|
||||
|
||||
// Flush forces a flush of all the queued dogstatsd payloads.
|
||||
Flush() error
|
||||
|
||||
// SetWriteTimeout allows the user to set a custom write timeout.
|
||||
SetWriteTimeout(d time.Duration) error
|
||||
}
|
||||
|
||||
// A Client is a handle for sending messages to dogstatsd. It is safe to
|
||||
// use one Client from multiple goroutines simultaneously.
|
||||
type Client struct {
|
||||
// Sender handles the underlying networking protocol
|
||||
sender *sender
|
||||
// Namespace to prepend to all statsd calls
|
||||
Namespace string
|
||||
// Tags are global tags to be added to every statsd call
|
||||
Tags []string
|
||||
// skipErrors turns off error passing and allows UDS to emulate UDP behaviour
|
||||
SkipErrors bool
|
||||
flushTime time.Duration
|
||||
metrics *ClientMetrics
|
||||
telemetry *telemetryClient
|
||||
stop chan struct{}
|
||||
wg sync.WaitGroup
|
||||
workers []*worker
|
||||
closerLock sync.Mutex
|
||||
workersMode ReceivingMode
|
||||
aggregatorMode ReceivingMode
|
||||
agg *aggregator
|
||||
aggExtended *aggregator
|
||||
options []Option
|
||||
addrOption string
|
||||
}
|
||||
|
||||
// ClientMetrics contains metrics about the client
|
||||
type ClientMetrics struct {
|
||||
TotalMetrics uint64
|
||||
TotalMetricsGauge uint64
|
||||
TotalMetricsCount uint64
|
||||
TotalMetricsHistogram uint64
|
||||
TotalMetricsDistribution uint64
|
||||
TotalMetricsSet uint64
|
||||
TotalMetricsTiming uint64
|
||||
TotalEvents uint64
|
||||
TotalServiceChecks uint64
|
||||
TotalDroppedOnReceive uint64
|
||||
}
|
||||
|
||||
// Verify that Client implements the ClientInterface.
|
||||
// https://golang.org/doc/faq#guarantee_satisfies_interface
|
||||
var _ ClientInterface = &Client{}
|
||||
|
||||
func resolveAddr(addr string) string {
|
||||
envPort := ""
|
||||
if addr == "" {
|
||||
addr = os.Getenv(agentHostEnvVarName)
|
||||
envPort = os.Getenv(agentPortEnvVarName)
|
||||
}
|
||||
|
||||
if addr == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(addr, WindowsPipeAddressPrefix) && !strings.HasPrefix(addr, UnixAddressPrefix) {
|
||||
if !strings.Contains(addr, ":") {
|
||||
if envPort != "" {
|
||||
addr = fmt.Sprintf("%s:%s", addr, envPort)
|
||||
} else {
|
||||
addr = fmt.Sprintf("%s:%s", addr, defaultUDPPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func createWriter(addr string) (statsdWriter, string, error) {
|
||||
addr = resolveAddr(addr)
|
||||
if addr == "" {
|
||||
return nil, "", errors.New("No address passed and autodetection from environment failed")
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(addr, WindowsPipeAddressPrefix):
|
||||
w, err := newWindowsPipeWriter(addr)
|
||||
return w, WriterWindowsPipe, err
|
||||
case strings.HasPrefix(addr, UnixAddressPrefix):
|
||||
w, err := newUDSWriter(addr[len(UnixAddressPrefix):])
|
||||
return w, WriterNameUDS, err
|
||||
default:
|
||||
w, err := newUDPWriter(addr)
|
||||
return w, WriterNameUDP, err
|
||||
}
|
||||
}
|
||||
|
||||
// New returns a pointer to a new Client given an addr in the format "hostname:port" for UDP,
|
||||
// "unix:///path/to/socket" for UDS or "\\.\pipe\path\to\pipe" for Windows Named Pipes.
|
||||
func New(addr string, options ...Option) (*Client, error) {
|
||||
o, err := resolveOptions(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w, writerType, err := createWriter(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := newWithWriter(w, o, writerType)
|
||||
if err == nil {
|
||||
client.options = append(client.options, options...)
|
||||
client.addrOption = addr
|
||||
}
|
||||
return client, err
|
||||
}
|
||||
|
||||
// NewWithWriter creates a new Client with given writer. Writer is a
|
||||
// io.WriteCloser + SetWriteTimeout(time.Duration) error
|
||||
func NewWithWriter(w statsdWriter, options ...Option) (*Client, error) {
|
||||
o, err := resolveOptions(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newWithWriter(w, o, "custom")
|
||||
}
|
||||
|
||||
// CloneWithExtraOptions create a new Client with extra options
|
||||
func CloneWithExtraOptions(c *Client, options ...Option) (*Client, error) {
|
||||
if c == nil {
|
||||
return nil, ErrNoClient
|
||||
}
|
||||
|
||||
if c.addrOption == "" {
|
||||
return nil, fmt.Errorf("can't clone client with no addrOption")
|
||||
}
|
||||
opt := append(c.options, options...)
|
||||
return New(c.addrOption, opt...)
|
||||
}
|
||||
|
||||
func newWithWriter(w statsdWriter, o *Options, writerName string) (*Client, error) {
|
||||
|
||||
w.SetWriteTimeout(o.WriteTimeoutUDS)
|
||||
|
||||
c := Client{
|
||||
Namespace: o.Namespace,
|
||||
Tags: o.Tags,
|
||||
metrics: &ClientMetrics{},
|
||||
}
|
||||
// Inject values of DD_* environment variables as global tags.
|
||||
for _, mapping := range ddEnvTagsMapping {
|
||||
if value := os.Getenv(mapping.envName); value != "" {
|
||||
c.Tags = append(c.Tags, fmt.Sprintf("%s:%s", mapping.tagName, value))
|
||||
}
|
||||
}
|
||||
|
||||
if o.MaxBytesPerPayload == 0 {
|
||||
if writerName == WriterNameUDS {
|
||||
o.MaxBytesPerPayload = DefaultMaxAgentPayloadSize
|
||||
} else {
|
||||
o.MaxBytesPerPayload = OptimalUDPPayloadSize
|
||||
}
|
||||
}
|
||||
if o.BufferPoolSize == 0 {
|
||||
if writerName == WriterNameUDS {
|
||||
o.BufferPoolSize = DefaultUDSBufferPoolSize
|
||||
} else {
|
||||
o.BufferPoolSize = DefaultUDPBufferPoolSize
|
||||
}
|
||||
}
|
||||
if o.SenderQueueSize == 0 {
|
||||
if writerName == WriterNameUDS {
|
||||
o.SenderQueueSize = DefaultUDSBufferPoolSize
|
||||
} else {
|
||||
o.SenderQueueSize = DefaultUDPBufferPoolSize
|
||||
}
|
||||
}
|
||||
|
||||
bufferPool := newBufferPool(o.BufferPoolSize, o.MaxBytesPerPayload, o.MaxMessagesPerPayload)
|
||||
c.sender = newSender(w, o.SenderQueueSize, bufferPool)
|
||||
c.aggregatorMode = o.ReceiveMode
|
||||
|
||||
c.workersMode = o.ReceiveMode
|
||||
// ChannelMode mode at the worker level is not enabled when
|
||||
// ExtendedAggregation is since the user app will not directly
|
||||
// use the worker (the aggregator sit between the app and the
|
||||
// workers).
|
||||
if o.ExtendedAggregation {
|
||||
c.workersMode = MutexMode
|
||||
}
|
||||
|
||||
if o.Aggregation || o.ExtendedAggregation {
|
||||
c.agg = newAggregator(&c)
|
||||
c.agg.start(o.AggregationFlushInterval)
|
||||
|
||||
if o.ExtendedAggregation {
|
||||
c.aggExtended = c.agg
|
||||
|
||||
if c.aggregatorMode == ChannelMode {
|
||||
c.agg.startReceivingMetric(o.ChannelModeBufferSize, o.BufferShardCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < o.BufferShardCount; i++ {
|
||||
w := newWorker(bufferPool, c.sender)
|
||||
c.workers = append(c.workers, w)
|
||||
|
||||
if c.workersMode == ChannelMode {
|
||||
w.startReceivingMetric(o.ChannelModeBufferSize)
|
||||
}
|
||||
}
|
||||
|
||||
c.flushTime = o.BufferFlushInterval
|
||||
c.stop = make(chan struct{}, 1)
|
||||
|
||||
c.wg.Add(1)
|
||||
go func() {
|
||||
defer c.wg.Done()
|
||||
c.watch()
|
||||
}()
|
||||
|
||||
if o.Telemetry {
|
||||
if o.TelemetryAddr == "" {
|
||||
c.telemetry = newTelemetryClient(&c, writerName, o.DevMode)
|
||||
} else {
|
||||
var err error
|
||||
c.telemetry, err = newTelemetryClientWithCustomAddr(&c, writerName, o.DevMode, o.TelemetryAddr, bufferPool)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
c.telemetry.run(&c.wg, c.stop)
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// NewBuffered returns a Client that buffers its output and sends it in chunks.
|
||||
// Buflen is the length of the buffer in number of commands.
|
||||
//
|
||||
// When addr is empty, the client will default to a UDP client and use the DD_AGENT_HOST
|
||||
// and (optionally) the DD_DOGSTATSD_PORT environment variables to build the target address.
|
||||
func NewBuffered(addr string, buflen int) (*Client, error) {
|
||||
return New(addr, WithMaxMessagesPerPayload(buflen))
|
||||
}
|
||||
|
||||
// SetWriteTimeout allows the user to set a custom UDS write timeout. Not supported for UDP
|
||||
// or Windows Pipes.
|
||||
func (c *Client) SetWriteTimeout(d time.Duration) error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
return c.sender.transport.SetWriteTimeout(d)
|
||||
}
|
||||
|
||||
func (c *Client) watch() {
|
||||
ticker := time.NewTicker(c.flushTime)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
for _, w := range c.workers {
|
||||
w.flush()
|
||||
}
|
||||
case <-c.stop:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flush forces a flush of all the queued dogstatsd payloads This method is
|
||||
// blocking and will not return until everything is sent through the network.
|
||||
// In MutexMode, this will also block sampling new data to the client while the
|
||||
// workers and sender are flushed.
|
||||
func (c *Client) Flush() error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
if c.agg != nil {
|
||||
c.agg.flush()
|
||||
}
|
||||
for _, w := range c.workers {
|
||||
w.pause()
|
||||
defer w.unpause()
|
||||
w.flushUnsafe()
|
||||
}
|
||||
// Now that the worker are pause the sender can flush the queue between
|
||||
// worker and senders
|
||||
c.sender.flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) FlushTelemetryMetrics() ClientMetrics {
|
||||
cm := ClientMetrics{
|
||||
TotalMetricsGauge: atomic.SwapUint64(&c.metrics.TotalMetricsGauge, 0),
|
||||
TotalMetricsCount: atomic.SwapUint64(&c.metrics.TotalMetricsCount, 0),
|
||||
TotalMetricsSet: atomic.SwapUint64(&c.metrics.TotalMetricsSet, 0),
|
||||
TotalMetricsHistogram: atomic.SwapUint64(&c.metrics.TotalMetricsHistogram, 0),
|
||||
TotalMetricsDistribution: atomic.SwapUint64(&c.metrics.TotalMetricsDistribution, 0),
|
||||
TotalMetricsTiming: atomic.SwapUint64(&c.metrics.TotalMetricsTiming, 0),
|
||||
TotalEvents: atomic.SwapUint64(&c.metrics.TotalEvents, 0),
|
||||
TotalServiceChecks: atomic.SwapUint64(&c.metrics.TotalServiceChecks, 0),
|
||||
TotalDroppedOnReceive: atomic.SwapUint64(&c.metrics.TotalDroppedOnReceive, 0),
|
||||
}
|
||||
|
||||
cm.TotalMetrics = cm.TotalMetricsGauge + cm.TotalMetricsCount +
|
||||
cm.TotalMetricsSet + cm.TotalMetricsHistogram +
|
||||
cm.TotalMetricsDistribution + cm.TotalMetricsTiming
|
||||
|
||||
return cm
|
||||
}
|
||||
|
||||
func (c *Client) send(m metric) error {
|
||||
h := hashString32(m.name)
|
||||
worker := c.workers[h%uint32(len(c.workers))]
|
||||
|
||||
if c.workersMode == ChannelMode {
|
||||
select {
|
||||
case worker.inputMetrics <- m:
|
||||
default:
|
||||
atomic.AddUint64(&c.metrics.TotalDroppedOnReceive, 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return worker.processMetric(m)
|
||||
}
|
||||
|
||||
// sendBlocking is used by the aggregator to inject aggregated metrics.
|
||||
func (c *Client) sendBlocking(m metric) error {
|
||||
m.globalTags = c.Tags
|
||||
m.namespace = c.Namespace
|
||||
|
||||
h := hashString32(m.name)
|
||||
worker := c.workers[h%uint32(len(c.workers))]
|
||||
return worker.processMetric(m)
|
||||
}
|
||||
|
||||
func (c *Client) sendToAggregator(mType metricType, name string, value float64, tags []string, rate float64, f bufferedMetricSampleFunc) error {
|
||||
if c.aggregatorMode == ChannelMode {
|
||||
select {
|
||||
case c.aggExtended.inputMetrics <- metric{metricType: mType, name: name, fvalue: value, tags: tags, rate: rate}:
|
||||
default:
|
||||
atomic.AddUint64(&c.metrics.TotalDroppedOnReceive, 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return f(name, value, tags, rate)
|
||||
}
|
||||
|
||||
// Gauge measures the value of a metric at a particular time.
|
||||
func (c *Client) Gauge(name string, value float64, tags []string, rate float64) error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
atomic.AddUint64(&c.metrics.TotalMetricsGauge, 1)
|
||||
if c.agg != nil {
|
||||
return c.agg.gauge(name, value, tags)
|
||||
}
|
||||
return c.send(metric{metricType: gauge, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace})
|
||||
}
|
||||
|
||||
// Count tracks how many times something happened per second.
|
||||
func (c *Client) Count(name string, value int64, tags []string, rate float64) error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
atomic.AddUint64(&c.metrics.TotalMetricsCount, 1)
|
||||
if c.agg != nil {
|
||||
return c.agg.count(name, value, tags)
|
||||
}
|
||||
return c.send(metric{metricType: count, name: name, ivalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace})
|
||||
}
|
||||
|
||||
// Histogram tracks the statistical distribution of a set of values on each host.
|
||||
func (c *Client) Histogram(name string, value float64, tags []string, rate float64) error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
atomic.AddUint64(&c.metrics.TotalMetricsHistogram, 1)
|
||||
if c.aggExtended != nil {
|
||||
return c.sendToAggregator(histogram, name, value, tags, rate, c.aggExtended.histogram)
|
||||
}
|
||||
return c.send(metric{metricType: histogram, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace})
|
||||
}
|
||||
|
||||
// Distribution tracks the statistical distribution of a set of values across your infrastructure.
|
||||
func (c *Client) Distribution(name string, value float64, tags []string, rate float64) error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
atomic.AddUint64(&c.metrics.TotalMetricsDistribution, 1)
|
||||
if c.aggExtended != nil {
|
||||
return c.sendToAggregator(distribution, name, value, tags, rate, c.aggExtended.distribution)
|
||||
}
|
||||
return c.send(metric{metricType: distribution, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace})
|
||||
}
|
||||
|
||||
// Decr is just Count of -1
|
||||
func (c *Client) Decr(name string, tags []string, rate float64) error {
|
||||
return c.Count(name, -1, tags, rate)
|
||||
}
|
||||
|
||||
// Incr is just Count of 1
|
||||
func (c *Client) Incr(name string, tags []string, rate float64) error {
|
||||
return c.Count(name, 1, tags, rate)
|
||||
}
|
||||
|
||||
// Set counts the number of unique elements in a group.
|
||||
func (c *Client) Set(name string, value string, tags []string, rate float64) error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
atomic.AddUint64(&c.metrics.TotalMetricsSet, 1)
|
||||
if c.agg != nil {
|
||||
return c.agg.set(name, value, tags)
|
||||
}
|
||||
return c.send(metric{metricType: set, name: name, svalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace})
|
||||
}
|
||||
|
||||
// Timing sends timing information, it is an alias for TimeInMilliseconds
|
||||
func (c *Client) Timing(name string, value time.Duration, tags []string, rate float64) error {
|
||||
return c.TimeInMilliseconds(name, value.Seconds()*1000, tags, rate)
|
||||
}
|
||||
|
||||
// TimeInMilliseconds sends timing information in milliseconds.
|
||||
// It is flushed by statsd with percentiles, mean and other info (https://github.com/etsy/statsd/blob/master/docs/metric_types.md#timing)
|
||||
func (c *Client) TimeInMilliseconds(name string, value float64, tags []string, rate float64) error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
atomic.AddUint64(&c.metrics.TotalMetricsTiming, 1)
|
||||
if c.aggExtended != nil {
|
||||
return c.sendToAggregator(timing, name, value, tags, rate, c.aggExtended.timing)
|
||||
}
|
||||
return c.send(metric{metricType: timing, name: name, fvalue: value, tags: tags, rate: rate, globalTags: c.Tags, namespace: c.Namespace})
|
||||
}
|
||||
|
||||
// Event sends the provided Event.
|
||||
func (c *Client) Event(e *Event) error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
atomic.AddUint64(&c.metrics.TotalEvents, 1)
|
||||
return c.send(metric{metricType: event, evalue: e, rate: 1, globalTags: c.Tags, namespace: c.Namespace})
|
||||
}
|
||||
|
||||
// SimpleEvent sends an event with the provided title and text.
|
||||
func (c *Client) SimpleEvent(title, text string) error {
|
||||
e := NewEvent(title, text)
|
||||
return c.Event(e)
|
||||
}
|
||||
|
||||
// ServiceCheck sends the provided ServiceCheck.
|
||||
func (c *Client) ServiceCheck(sc *ServiceCheck) error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
atomic.AddUint64(&c.metrics.TotalServiceChecks, 1)
|
||||
return c.send(metric{metricType: serviceCheck, scvalue: sc, rate: 1, globalTags: c.Tags, namespace: c.Namespace})
|
||||
}
|
||||
|
||||
// SimpleServiceCheck sends an serviceCheck with the provided name and status.
|
||||
func (c *Client) SimpleServiceCheck(name string, status ServiceCheckStatus) error {
|
||||
sc := NewServiceCheck(name, status)
|
||||
return c.ServiceCheck(sc)
|
||||
}
|
||||
|
||||
// Close the client connection.
|
||||
func (c *Client) Close() error {
|
||||
if c == nil {
|
||||
return ErrNoClient
|
||||
}
|
||||
|
||||
// Acquire closer lock to ensure only one thread can close the stop channel
|
||||
c.closerLock.Lock()
|
||||
defer c.closerLock.Unlock()
|
||||
|
||||
// Notify all other threads that they should stop
|
||||
select {
|
||||
case <-c.stop:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
close(c.stop)
|
||||
|
||||
if c.workersMode == ChannelMode {
|
||||
for _, w := range c.workers {
|
||||
w.stopReceivingMetric()
|
||||
}
|
||||
}
|
||||
|
||||
// flush the aggregator first
|
||||
if c.agg != nil {
|
||||
if c.aggExtended != nil && c.aggregatorMode == ChannelMode {
|
||||
c.agg.stopReceivingMetric()
|
||||
}
|
||||
c.agg.stop()
|
||||
}
|
||||
|
||||
// Wait for the threads to stop
|
||||
c.wg.Wait()
|
||||
|
||||
c.Flush()
|
||||
return c.sender.close()
|
||||
}
|
||||
151
vendor/github.com/DataDog/datadog-go/statsd/telemetry.go
generated
vendored
Normal file
151
vendor/github.com/DataDog/datadog-go/statsd/telemetry.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
TelemetryInterval is the interval at which telemetry will be sent by the client.
|
||||
*/
|
||||
const TelemetryInterval = 10 * time.Second
|
||||
|
||||
/*
|
||||
clientTelemetryTag is a tag identifying this specific client.
|
||||
*/
|
||||
var clientTelemetryTag = "client:go"
|
||||
|
||||
/*
|
||||
clientVersionTelemetryTag is a tag identifying this specific client version.
|
||||
*/
|
||||
var clientVersionTelemetryTag = "client_version:4.8.2"
|
||||
|
||||
type telemetryClient struct {
|
||||
c *Client
|
||||
tags []string
|
||||
tagsByType map[metricType][]string
|
||||
sender *sender
|
||||
worker *worker
|
||||
devMode bool
|
||||
}
|
||||
|
||||
func newTelemetryClient(c *Client, transport string, devMode bool) *telemetryClient {
|
||||
t := &telemetryClient{
|
||||
c: c,
|
||||
tags: append(c.Tags, clientTelemetryTag, clientVersionTelemetryTag, "client_transport:"+transport),
|
||||
tagsByType: map[metricType][]string{},
|
||||
devMode: devMode,
|
||||
}
|
||||
|
||||
if devMode {
|
||||
t.tagsByType[gauge] = append(append([]string{}, t.tags...), "metrics_type:gauge")
|
||||
t.tagsByType[count] = append(append([]string{}, t.tags...), "metrics_type:count")
|
||||
t.tagsByType[set] = append(append([]string{}, t.tags...), "metrics_type:set")
|
||||
t.tagsByType[timing] = append(append([]string{}, t.tags...), "metrics_type:timing")
|
||||
t.tagsByType[histogram] = append(append([]string{}, t.tags...), "metrics_type:histogram")
|
||||
t.tagsByType[distribution] = append(append([]string{}, t.tags...), "metrics_type:distribution")
|
||||
t.tagsByType[timing] = append(append([]string{}, t.tags...), "metrics_type:timing")
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func newTelemetryClientWithCustomAddr(c *Client, transport string, devMode bool, telemetryAddr string, pool *bufferPool) (*telemetryClient, error) {
|
||||
telemetryWriter, _, err := createWriter(telemetryAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not resolve telemetry address: %v", err)
|
||||
}
|
||||
|
||||
t := newTelemetryClient(c, transport, devMode)
|
||||
|
||||
// Creating a custom sender/worker with 1 worker in mutex mode for the
|
||||
// telemetry that share the same bufferPool.
|
||||
// FIXME due to performance pitfall, we're always using UDP defaults
|
||||
// even for UDS.
|
||||
t.sender = newSender(telemetryWriter, DefaultUDPBufferPoolSize, pool)
|
||||
t.worker = newWorker(pool, t.sender)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *telemetryClient) run(wg *sync.WaitGroup, stop chan struct{}) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
ticker := time.NewTicker(TelemetryInterval)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
t.sendTelemetry()
|
||||
case <-stop:
|
||||
ticker.Stop()
|
||||
if t.sender != nil {
|
||||
t.sender.close()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (t *telemetryClient) sendTelemetry() {
|
||||
for _, m := range t.flush() {
|
||||
if t.worker != nil {
|
||||
t.worker.processMetric(m)
|
||||
} else {
|
||||
t.c.send(m)
|
||||
}
|
||||
}
|
||||
|
||||
if t.worker != nil {
|
||||
t.worker.flush()
|
||||
}
|
||||
}
|
||||
|
||||
// flushTelemetry returns Telemetry metrics to be flushed. It's its own function to ease testing.
|
||||
func (t *telemetryClient) flush() []metric {
|
||||
m := []metric{}
|
||||
|
||||
// same as Count but without global namespace
|
||||
telemetryCount := func(name string, value int64, tags []string) {
|
||||
m = append(m, metric{metricType: count, name: name, ivalue: value, tags: tags, rate: 1})
|
||||
}
|
||||
|
||||
clientMetrics := t.c.FlushTelemetryMetrics()
|
||||
telemetryCount("datadog.dogstatsd.client.metrics", int64(clientMetrics.TotalMetrics), t.tags)
|
||||
if t.devMode {
|
||||
telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsGauge), t.tagsByType[gauge])
|
||||
telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsCount), t.tagsByType[count])
|
||||
telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsHistogram), t.tagsByType[histogram])
|
||||
telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsDistribution), t.tagsByType[distribution])
|
||||
telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsSet), t.tagsByType[set])
|
||||
telemetryCount("datadog.dogstatsd.client.metrics_by_type", int64(clientMetrics.TotalMetricsTiming), t.tagsByType[timing])
|
||||
}
|
||||
|
||||
telemetryCount("datadog.dogstatsd.client.events", int64(clientMetrics.TotalEvents), t.tags)
|
||||
telemetryCount("datadog.dogstatsd.client.service_checks", int64(clientMetrics.TotalServiceChecks), t.tags)
|
||||
telemetryCount("datadog.dogstatsd.client.metric_dropped_on_receive", int64(clientMetrics.TotalDroppedOnReceive), t.tags)
|
||||
|
||||
senderMetrics := t.c.sender.flushTelemetryMetrics()
|
||||
telemetryCount("datadog.dogstatsd.client.packets_sent", int64(senderMetrics.TotalSentPayloads), t.tags)
|
||||
telemetryCount("datadog.dogstatsd.client.bytes_sent", int64(senderMetrics.TotalSentBytes), t.tags)
|
||||
telemetryCount("datadog.dogstatsd.client.packets_dropped", int64(senderMetrics.TotalDroppedPayloads), t.tags)
|
||||
telemetryCount("datadog.dogstatsd.client.bytes_dropped", int64(senderMetrics.TotalDroppedBytes), t.tags)
|
||||
telemetryCount("datadog.dogstatsd.client.packets_dropped_queue", int64(senderMetrics.TotalDroppedPayloadsQueueFull), t.tags)
|
||||
telemetryCount("datadog.dogstatsd.client.bytes_dropped_queue", int64(senderMetrics.TotalDroppedBytesQueueFull), t.tags)
|
||||
telemetryCount("datadog.dogstatsd.client.packets_dropped_writer", int64(senderMetrics.TotalDroppedPayloadsWriter), t.tags)
|
||||
telemetryCount("datadog.dogstatsd.client.bytes_dropped_writer", int64(senderMetrics.TotalDroppedBytesWriter), t.tags)
|
||||
|
||||
if aggMetrics := t.c.agg.flushTelemetryMetrics(); aggMetrics != nil {
|
||||
telemetryCount("datadog.dogstatsd.client.aggregated_context", int64(aggMetrics.nbContext), t.tags)
|
||||
if t.devMode {
|
||||
telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextGauge), t.tagsByType[gauge])
|
||||
telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextSet), t.tagsByType[set])
|
||||
telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextCount), t.tagsByType[count])
|
||||
telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextHistogram), t.tagsByType[histogram])
|
||||
telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextDistribution), t.tagsByType[distribution])
|
||||
telemetryCount("datadog.dogstatsd.client.aggregated_context_by_type", int64(aggMetrics.nbContextTiming), t.tagsByType[timing])
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
40
vendor/github.com/DataDog/datadog-go/statsd/udp.go
generated
vendored
Normal file
40
vendor/github.com/DataDog/datadog-go/statsd/udp.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// udpWriter is an internal class wrapping around management of UDP connection
|
||||
type udpWriter struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
// New returns a pointer to a new udpWriter given an addr in the format "hostname:port".
|
||||
func newUDPWriter(addr string) (*udpWriter, error) {
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.DialUDP("udp", nil, udpAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writer := &udpWriter{conn: conn}
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
// SetWriteTimeout is not needed for UDP, returns error
|
||||
func (w *udpWriter) SetWriteTimeout(d time.Duration) error {
|
||||
return errors.New("SetWriteTimeout: not supported for UDP connections")
|
||||
}
|
||||
|
||||
// Write data to the UDP connection with no error handling
|
||||
func (w *udpWriter) Write(data []byte) (int, error) {
|
||||
return w.conn.Write(data)
|
||||
}
|
||||
|
||||
func (w *udpWriter) Close() error {
|
||||
return w.conn.Close()
|
||||
}
|
||||
100
vendor/github.com/DataDog/datadog-go/statsd/uds.go
generated
vendored
Normal file
100
vendor/github.com/DataDog/datadog-go/statsd/uds.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
// +build !windows
|
||||
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
UDSTimeout holds the default timeout for UDS socket writes, as they can get
|
||||
blocking when the receiving buffer is full.
|
||||
*/
|
||||
const defaultUDSTimeout = 100 * time.Millisecond
|
||||
|
||||
// udsWriter is an internal class wrapping around management of UDS connection
|
||||
type udsWriter struct {
|
||||
// Address to send metrics to, needed to allow reconnection on error
|
||||
addr net.Addr
|
||||
// Established connection object, or nil if not connected yet
|
||||
conn net.Conn
|
||||
// write timeout
|
||||
writeTimeout time.Duration
|
||||
sync.RWMutex // used to lock conn / writer can replace it
|
||||
}
|
||||
|
||||
// newUDSWriter returns a pointer to a new udsWriter given a socket file path as addr.
|
||||
func newUDSWriter(addr string) (*udsWriter, error) {
|
||||
udsAddr, err := net.ResolveUnixAddr("unixgram", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Defer connection to first Write
|
||||
writer := &udsWriter{addr: udsAddr, conn: nil, writeTimeout: defaultUDSTimeout}
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
// SetWriteTimeout allows the user to set a custom write timeout
|
||||
func (w *udsWriter) SetWriteTimeout(d time.Duration) error {
|
||||
w.writeTimeout = d
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write data to the UDS connection with write timeout and minimal error handling:
|
||||
// create the connection if nil, and destroy it if the statsd server has disconnected
|
||||
func (w *udsWriter) Write(data []byte) (int, error) {
|
||||
conn, err := w.ensureConnection()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
conn.SetWriteDeadline(time.Now().Add(w.writeTimeout))
|
||||
n, e := conn.Write(data)
|
||||
|
||||
if err, isNetworkErr := e.(net.Error); err != nil && (!isNetworkErr || !err.Temporary()) {
|
||||
// Statsd server disconnected, retry connecting at next packet
|
||||
w.unsetConnection()
|
||||
return 0, e
|
||||
}
|
||||
return n, e
|
||||
}
|
||||
|
||||
func (w *udsWriter) Close() error {
|
||||
if w.conn != nil {
|
||||
return w.conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *udsWriter) ensureConnection() (net.Conn, error) {
|
||||
// Check if we've already got a socket we can use
|
||||
w.RLock()
|
||||
currentConn := w.conn
|
||||
w.RUnlock()
|
||||
|
||||
if currentConn != nil {
|
||||
return currentConn, nil
|
||||
}
|
||||
|
||||
// Looks like we might need to connect - try again with write locking.
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
if w.conn != nil {
|
||||
return w.conn, nil
|
||||
}
|
||||
|
||||
newConn, err := net.Dial(w.addr.Network(), w.addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w.conn = newConn
|
||||
return newConn, nil
|
||||
}
|
||||
|
||||
func (w *udsWriter) unsetConnection() {
|
||||
w.Lock()
|
||||
defer w.Unlock()
|
||||
w.conn = nil
|
||||
}
|
||||
10
vendor/github.com/DataDog/datadog-go/statsd/uds_windows.go
generated
vendored
Normal file
10
vendor/github.com/DataDog/datadog-go/statsd/uds_windows.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// +build windows
|
||||
|
||||
package statsd
|
||||
|
||||
import "fmt"
|
||||
|
||||
// newUDSWriter is disable on windows as unix sockets are not available
|
||||
func newUDSWriter(addr string) (statsdWriter, error) {
|
||||
return nil, fmt.Errorf("unix socket is not available on windows")
|
||||
}
|
||||
23
vendor/github.com/DataDog/datadog-go/statsd/utils.go
generated
vendored
Normal file
23
vendor/github.com/DataDog/datadog-go/statsd/utils.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func shouldSample(rate float64, r *rand.Rand, lock *sync.Mutex) bool {
|
||||
if rate >= 1 {
|
||||
return true
|
||||
}
|
||||
// sources created by rand.NewSource() (ie. w.random) are not thread safe.
|
||||
// TODO: use defer once the lowest Go version we support is 1.14 (defer
|
||||
// has an overhead before that).
|
||||
lock.Lock()
|
||||
if r.Float64() > rate {
|
||||
lock.Unlock()
|
||||
return false
|
||||
}
|
||||
lock.Unlock()
|
||||
return true
|
||||
|
||||
}
|
||||
150
vendor/github.com/DataDog/datadog-go/statsd/worker.go
generated
vendored
Normal file
150
vendor/github.com/DataDog/datadog-go/statsd/worker.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type worker struct {
|
||||
pool *bufferPool
|
||||
buffer *statsdBuffer
|
||||
sender *sender
|
||||
random *rand.Rand
|
||||
randomLock sync.Mutex
|
||||
sync.Mutex
|
||||
|
||||
inputMetrics chan metric
|
||||
stop chan struct{}
|
||||
}
|
||||
|
||||
func newWorker(pool *bufferPool, sender *sender) *worker {
|
||||
// Each worker uses its own random source and random lock to prevent
|
||||
// workers in separate goroutines from contending for the lock on the
|
||||
// "math/rand" package-global random source (e.g. calls like
|
||||
// "rand.Float64()" must acquire a shared lock to get the next
|
||||
// pseudorandom number).
|
||||
// Note that calling "time.Now().UnixNano()" repeatedly quickly may return
|
||||
// very similar values. That's fine for seeding the worker-specific random
|
||||
// source because we just need an evenly distributed stream of float values.
|
||||
// Do not use this random source for cryptographic randomness.
|
||||
random := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
return &worker{
|
||||
pool: pool,
|
||||
sender: sender,
|
||||
buffer: pool.borrowBuffer(),
|
||||
random: random,
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *worker) startReceivingMetric(bufferSize int) {
|
||||
w.inputMetrics = make(chan metric, bufferSize)
|
||||
go w.pullMetric()
|
||||
}
|
||||
|
||||
func (w *worker) stopReceivingMetric() {
|
||||
w.stop <- struct{}{}
|
||||
}
|
||||
|
||||
func (w *worker) pullMetric() {
|
||||
for {
|
||||
select {
|
||||
case m := <-w.inputMetrics:
|
||||
w.processMetric(m)
|
||||
case <-w.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *worker) processMetric(m metric) error {
|
||||
if !shouldSample(m.rate, w.random, &w.randomLock) {
|
||||
return nil
|
||||
}
|
||||
w.Lock()
|
||||
var err error
|
||||
if err = w.writeMetricUnsafe(m); err == errBufferFull {
|
||||
w.flushUnsafe()
|
||||
err = w.writeMetricUnsafe(m)
|
||||
}
|
||||
w.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *worker) writeAggregatedMetricUnsafe(m metric, metricSymbol []byte, precision int) error {
|
||||
globalPos := 0
|
||||
|
||||
// first check how much data we can write to the buffer:
|
||||
// +3 + len(metricSymbol) because the message will include '|<metricSymbol>|#' before the tags
|
||||
// +1 for the potential line break at the start of the metric
|
||||
tagsSize := len(m.stags) + 4 + len(metricSymbol)
|
||||
for _, t := range m.globalTags {
|
||||
tagsSize += len(t) + 1
|
||||
}
|
||||
|
||||
for {
|
||||
pos, err := w.buffer.writeAggregated(metricSymbol, m.namespace, m.globalTags, m.name, m.fvalues[globalPos:], m.stags, tagsSize, precision)
|
||||
if err == errPartialWrite {
|
||||
// We successfully wrote part of the histogram metrics.
|
||||
// We flush the current buffer and finish the histogram
|
||||
// in a new one.
|
||||
w.flushUnsafe()
|
||||
globalPos += pos
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *worker) writeMetricUnsafe(m metric) error {
|
||||
switch m.metricType {
|
||||
case gauge:
|
||||
return w.buffer.writeGauge(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate)
|
||||
case count:
|
||||
return w.buffer.writeCount(m.namespace, m.globalTags, m.name, m.ivalue, m.tags, m.rate)
|
||||
case histogram:
|
||||
return w.buffer.writeHistogram(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate)
|
||||
case distribution:
|
||||
return w.buffer.writeDistribution(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate)
|
||||
case set:
|
||||
return w.buffer.writeSet(m.namespace, m.globalTags, m.name, m.svalue, m.tags, m.rate)
|
||||
case timing:
|
||||
return w.buffer.writeTiming(m.namespace, m.globalTags, m.name, m.fvalue, m.tags, m.rate)
|
||||
case event:
|
||||
return w.buffer.writeEvent(*m.evalue, m.globalTags)
|
||||
case serviceCheck:
|
||||
return w.buffer.writeServiceCheck(*m.scvalue, m.globalTags)
|
||||
case histogramAggregated:
|
||||
return w.writeAggregatedMetricUnsafe(m, histogramSymbol, -1)
|
||||
case distributionAggregated:
|
||||
return w.writeAggregatedMetricUnsafe(m, distributionSymbol, -1)
|
||||
case timingAggregated:
|
||||
return w.writeAggregatedMetricUnsafe(m, timingSymbol, 6)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *worker) flush() {
|
||||
w.Lock()
|
||||
w.flushUnsafe()
|
||||
w.Unlock()
|
||||
}
|
||||
|
||||
func (w *worker) pause() {
|
||||
w.Lock()
|
||||
}
|
||||
|
||||
func (w *worker) unpause() {
|
||||
w.Unlock()
|
||||
}
|
||||
|
||||
// flush the current buffer. Lock must be held by caller.
|
||||
// flushed buffer written to the network asynchronously.
|
||||
func (w *worker) flushUnsafe() {
|
||||
if len(w.buffer.bytes()) > 0 {
|
||||
w.sender.send(w.buffer)
|
||||
w.buffer = w.pool.borrowBuffer()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Evan Huus
|
||||
Copyright (c) 2015 Datadog, Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -19,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
4
vendor/github.com/DataDog/datadog-go/v5/statsd/README.md
generated
vendored
Normal file
4
vendor/github.com/DataDog/datadog-go/v5/statsd/README.md
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
## Overview
|
||||
|
||||
Package `statsd` provides a Go [dogstatsd](http://docs.datadoghq.com/guides/dogstatsd/) client. Dogstatsd extends Statsd, adding tags
|
||||
and histograms.
|
||||
269
vendor/github.com/DataDog/datadog-go/v5/statsd/aggregator.go
generated
vendored
Normal file
269
vendor/github.com/DataDog/datadog-go/v5/statsd/aggregator.go
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
countsMap map[string]*countMetric
|
||||
gaugesMap map[string]*gaugeMetric
|
||||
setsMap map[string]*setMetric
|
||||
bufferedMetricMap map[string]*bufferedMetric
|
||||
)
|
||||
|
||||
type aggregator struct {
|
||||
nbContextGauge uint64
|
||||
nbContextCount uint64
|
||||
nbContextSet uint64
|
||||
|
||||
countsM sync.RWMutex
|
||||
gaugesM sync.RWMutex
|
||||
setsM sync.RWMutex
|
||||
|
||||
gauges gaugesMap
|
||||
counts countsMap
|
||||
sets setsMap
|
||||
histograms bufferedMetricContexts
|
||||
distributions bufferedMetricContexts
|
||||
timings bufferedMetricContexts
|
||||
|
||||
closed chan struct{}
|
||||
|
||||
client *Client
|
||||
|
||||
// aggregator implements channelMode mechanism to receive histograms,
|
||||
// distributions and timings. Since they need sampling they need to
|
||||
// lock for random. When using both channelMode and ExtendedAggregation
|
||||
// we don't want goroutine to fight over the lock.
|
||||
inputMetrics chan metric
|
||||
stopChannelMode chan struct{}
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func newAggregator(c *Client) *aggregator {
|
||||
return &aggregator{
|
||||
client: c,
|
||||
counts: countsMap{},
|
||||
gauges: gaugesMap{},
|
||||
sets: setsMap{},
|
||||
histograms: newBufferedContexts(newHistogramMetric),
|
||||
distributions: newBufferedContexts(newDistributionMetric),
|
||||
timings: newBufferedContexts(newTimingMetric),
|
||||
closed: make(chan struct{}),
|
||||
stopChannelMode: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregator) start(flushInterval time.Duration) {
|
||||
ticker := time.NewTicker(flushInterval)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
a.flush()
|
||||
case <-a.closed:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (a *aggregator) startReceivingMetric(bufferSize int, nbWorkers int) {
|
||||
a.inputMetrics = make(chan metric, bufferSize)
|
||||
for i := 0; i < nbWorkers; i++ {
|
||||
a.wg.Add(1)
|
||||
go a.pullMetric()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregator) stopReceivingMetric() {
|
||||
close(a.stopChannelMode)
|
||||
a.wg.Wait()
|
||||
}
|
||||
|
||||
func (a *aggregator) stop() {
|
||||
a.closed <- struct{}{}
|
||||
}
|
||||
|
||||
func (a *aggregator) pullMetric() {
|
||||
for {
|
||||
select {
|
||||
case m := <-a.inputMetrics:
|
||||
switch m.metricType {
|
||||
case histogram:
|
||||
a.histogram(m.name, m.fvalue, m.tags, m.rate)
|
||||
case distribution:
|
||||
a.distribution(m.name, m.fvalue, m.tags, m.rate)
|
||||
case timing:
|
||||
a.timing(m.name, m.fvalue, m.tags, m.rate)
|
||||
}
|
||||
case <-a.stopChannelMode:
|
||||
a.wg.Done()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregator) flush() {
|
||||
for _, m := range a.flushMetrics() {
|
||||
a.client.sendBlocking(m)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregator) flushTelemetryMetrics(t *Telemetry) {
|
||||
if a == nil {
|
||||
// aggregation is disabled
|
||||
return
|
||||
}
|
||||
|
||||
t.AggregationNbContextGauge = atomic.LoadUint64(&a.nbContextGauge)
|
||||
t.AggregationNbContextCount = atomic.LoadUint64(&a.nbContextCount)
|
||||
t.AggregationNbContextSet = atomic.LoadUint64(&a.nbContextSet)
|
||||
t.AggregationNbContextHistogram = a.histograms.getNbContext()
|
||||
t.AggregationNbContextDistribution = a.distributions.getNbContext()
|
||||
t.AggregationNbContextTiming = a.timings.getNbContext()
|
||||
}
|
||||
|
||||
func (a *aggregator) flushMetrics() []metric {
|
||||
metrics := []metric{}
|
||||
|
||||
// We reset the values to avoid sending 'zero' values for metrics not
|
||||
// sampled during this flush interval
|
||||
|
||||
a.setsM.Lock()
|
||||
sets := a.sets
|
||||
a.sets = setsMap{}
|
||||
a.setsM.Unlock()
|
||||
|
||||
for _, s := range sets {
|
||||
metrics = append(metrics, s.flushUnsafe()...)
|
||||
}
|
||||
|
||||
a.gaugesM.Lock()
|
||||
gauges := a.gauges
|
||||
a.gauges = gaugesMap{}
|
||||
a.gaugesM.Unlock()
|
||||
|
||||
for _, g := range gauges {
|
||||
metrics = append(metrics, g.flushUnsafe())
|
||||
}
|
||||
|
||||
a.countsM.Lock()
|
||||
counts := a.counts
|
||||
a.counts = countsMap{}
|
||||
a.countsM.Unlock()
|
||||
|
||||
for _, c := range counts {
|
||||
metrics = append(metrics, c.flushUnsafe())
|
||||
}
|
||||
|
||||
metrics = a.histograms.flush(metrics)
|
||||
metrics = a.distributions.flush(metrics)
|
||||
metrics = a.timings.flush(metrics)
|
||||
|
||||
atomic.AddUint64(&a.nbContextCount, uint64(len(counts)))
|
||||
atomic.AddUint64(&a.nbContextGauge, uint64(len(gauges)))
|
||||
atomic.AddUint64(&a.nbContextSet, uint64(len(sets)))
|
||||
return metrics
|
||||
}
|
||||
|
||||
func getContext(name string, tags []string) string {
|
||||
return name + ":" + strings.Join(tags, tagSeparatorSymbol)
|
||||
}
|
||||
|
||||
func getContextAndTags(name string, tags []string) (string, string) {
|
||||
stringTags := strings.Join(tags, tagSeparatorSymbol)
|
||||
return name + ":" + stringTags, stringTags
|
||||
}
|
||||
|
||||
func (a *aggregator) count(name string, value int64, tags []string) error {
|
||||
context := getContext(name, tags)
|
||||
a.countsM.RLock()
|
||||
if count, found := a.counts[context]; found {
|
||||
count.sample(value)
|
||||
a.countsM.RUnlock()
|
||||
return nil
|
||||
}
|
||||
a.countsM.RUnlock()
|
||||
|
||||
a.countsM.Lock()
|
||||
// Check if another goroutines hasn't created the value betwen the RUnlock and 'Lock'
|
||||
if count, found := a.counts[context]; found {
|
||||
count.sample(value)
|
||||
a.countsM.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
a.counts[context] = newCountMetric(name, value, tags)
|
||||
a.countsM.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aggregator) gauge(name string, value float64, tags []string) error {
|
||||
context := getContext(name, tags)
|
||||
a.gaugesM.RLock()
|
||||
if gauge, found := a.gauges[context]; found {
|
||||
gauge.sample(value)
|
||||
a.gaugesM.RUnlock()
|
||||
return nil
|
||||
}
|
||||
a.gaugesM.RUnlock()
|
||||
|
||||
gauge := newGaugeMetric(name, value, tags)
|
||||
|
||||
a.gaugesM.Lock()
|
||||
// Check if another goroutines hasn't created the value betwen the 'RUnlock' and 'Lock'
|
||||
if gauge, found := a.gauges[context]; found {
|
||||
gauge.sample(value)
|
||||
a.gaugesM.Unlock()
|
||||
return nil
|
||||
}
|
||||
a.gauges[context] = gauge
|
||||
a.gaugesM.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *aggregator) set(name string, value string, tags []string) error {
|
||||
context := getContext(name, tags)
|
||||
a.setsM.RLock()
|
||||
if set, found := a.sets[context]; found {
|
||||
set.sample(value)
|
||||
a.setsM.RUnlock()
|
||||
return nil
|
||||
}
|
||||
a.setsM.RUnlock()
|
||||
|
||||
a.setsM.Lock()
|
||||
// Check if another goroutines hasn't created the value betwen the 'RUnlock' and 'Lock'
|
||||
if set, found := a.sets[context]; found {
|
||||
set.sample(value)
|
||||
a.setsM.Unlock()
|
||||
return nil
|
||||
}
|
||||
a.sets[context] = newSetMetric(name, value, tags)
|
||||
a.setsM.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Only histograms, distributions and timings are sampled with a rate since we
|
||||
// only pack them in on message instead of aggregating them. Discarding the
|
||||
// sample rate will have impacts on the CPU and memory usage of the Agent.
|
||||
|
||||
// type alias for Client.sendToAggregator
|
||||
type bufferedMetricSampleFunc func(name string, value float64, tags []string, rate float64) error
|
||||
|
||||
func (a *aggregator) histogram(name string, value float64, tags []string, rate float64) error {
|
||||
return a.histograms.sample(name, value, tags, rate)
|
||||
}
|
||||
|
||||
func (a *aggregator) distribution(name string, value float64, tags []string, rate float64) error {
|
||||
return a.distributions.sample(name, value, tags, rate)
|
||||
}
|
||||
|
||||
func (a *aggregator) timing(name string, value float64, tags []string, rate float64) error {
|
||||
return a.timings.sample(name, value, tags, rate)
|
||||
}
|
||||
190
vendor/github.com/DataDog/datadog-go/v5/statsd/buffer.go
generated
vendored
Normal file
190
vendor/github.com/DataDog/datadog-go/v5/statsd/buffer.go
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type bufferFullError string
|
||||
|
||||
func (e bufferFullError) Error() string { return string(e) }
|
||||
|
||||
const errBufferFull = bufferFullError("statsd buffer is full")
|
||||
|
||||
type partialWriteError string
|
||||
|
||||
func (e partialWriteError) Error() string { return string(e) }
|
||||
|
||||
const errPartialWrite = partialWriteError("value partially written")
|
||||
|
||||
const metricOverhead = 512
|
||||
|
||||
// statsdBuffer is a buffer containing statsd messages
|
||||
// this struct methods are NOT safe for concurent use
|
||||
type statsdBuffer struct {
|
||||
buffer []byte
|
||||
maxSize int
|
||||
maxElements int
|
||||
elementCount int
|
||||
}
|
||||
|
||||
func newStatsdBuffer(maxSize, maxElements int) *statsdBuffer {
|
||||
return &statsdBuffer{
|
||||
buffer: make([]byte, 0, maxSize+metricOverhead), // pre-allocate the needed size + metricOverhead to avoid having Go re-allocate on it's own if an element does not fit
|
||||
maxSize: maxSize,
|
||||
maxElements: maxElements,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeGauge(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendGauge(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeCount(namespace string, globalTags []string, name string, value int64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendCount(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeHistogram(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendHistogram(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
// writeAggregated serialized as many values as possible in the current buffer and return the position in values where it stopped.
|
||||
func (b *statsdBuffer) writeAggregated(metricSymbol []byte, namespace string, globalTags []string, name string, values []float64, tags string, tagSize int, precision int) (int, error) {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return 0, errBufferFull
|
||||
}
|
||||
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendHeader(b.buffer, namespace, name)
|
||||
|
||||
// buffer already full
|
||||
if len(b.buffer)+tagSize > b.maxSize {
|
||||
b.buffer = originalBuffer
|
||||
return 0, errBufferFull
|
||||
}
|
||||
|
||||
// We add as many value as possible
|
||||
var position int
|
||||
for idx, v := range values {
|
||||
previousBuffer := b.buffer
|
||||
if idx != 0 {
|
||||
b.buffer = append(b.buffer, ':')
|
||||
}
|
||||
|
||||
b.buffer = strconv.AppendFloat(b.buffer, v, 'f', precision, 64)
|
||||
|
||||
// Should we stop serializing and switch to another buffer
|
||||
if len(b.buffer)+tagSize > b.maxSize {
|
||||
b.buffer = previousBuffer
|
||||
break
|
||||
}
|
||||
position = idx + 1
|
||||
}
|
||||
|
||||
// we could not add a single value
|
||||
if position == 0 {
|
||||
b.buffer = originalBuffer
|
||||
return 0, errBufferFull
|
||||
}
|
||||
|
||||
b.buffer = append(b.buffer, '|')
|
||||
b.buffer = append(b.buffer, metricSymbol...)
|
||||
b.buffer = appendTagsAggregated(b.buffer, globalTags, tags)
|
||||
b.writeSeparator()
|
||||
b.elementCount++
|
||||
|
||||
if position != len(values) {
|
||||
return position, errPartialWrite
|
||||
}
|
||||
return position, nil
|
||||
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeDistribution(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendDistribution(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeSet(namespace string, globalTags []string, name string, value string, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendSet(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeTiming(namespace string, globalTags []string, name string, value float64, tags []string, rate float64) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendTiming(b.buffer, namespace, globalTags, name, value, tags, rate)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeEvent(event *Event, globalTags []string) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendEvent(b.buffer, event, globalTags)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeServiceCheck(serviceCheck *ServiceCheck, globalTags []string) error {
|
||||
if b.elementCount >= b.maxElements {
|
||||
return errBufferFull
|
||||
}
|
||||
originalBuffer := b.buffer
|
||||
b.buffer = appendServiceCheck(b.buffer, serviceCheck, globalTags)
|
||||
b.writeSeparator()
|
||||
return b.validateNewElement(originalBuffer)
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) validateNewElement(originalBuffer []byte) error {
|
||||
if len(b.buffer) > b.maxSize {
|
||||
b.buffer = originalBuffer
|
||||
return errBufferFull
|
||||
}
|
||||
b.elementCount++
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) writeSeparator() {
|
||||
b.buffer = append(b.buffer, '\n')
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) reset() {
|
||||
b.buffer = b.buffer[:0]
|
||||
b.elementCount = 0
|
||||
}
|
||||
|
||||
func (b *statsdBuffer) bytes() []byte {
|
||||
return b.buffer
|
||||
}
|
||||
40
vendor/github.com/DataDog/datadog-go/v5/statsd/buffer_pool.go
generated
vendored
Normal file
40
vendor/github.com/DataDog/datadog-go/v5/statsd/buffer_pool.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package statsd
|
||||
|
||||
type bufferPool struct {
|
||||
pool chan *statsdBuffer
|
||||
bufferMaxSize int
|
||||
bufferMaxElements int
|
||||
}
|
||||
|
||||
func newBufferPool(poolSize, bufferMaxSize, bufferMaxElements int) *bufferPool {
|
||||
p := &bufferPool{
|
||||
pool: make(chan *statsdBuffer, poolSize),
|
||||
bufferMaxSize: bufferMaxSize,
|
||||
bufferMaxElements: bufferMaxElements,
|
||||
}
|
||||
for i := 0; i < poolSize; i++ {
|
||||
p.addNewBuffer()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *bufferPool) addNewBuffer() {
|
||||
p.pool <- newStatsdBuffer(p.bufferMaxSize, p.bufferMaxElements)
|
||||
}
|
||||
|
||||
func (p *bufferPool) borrowBuffer() *statsdBuffer {
|
||||
select {
|
||||
case b := <-p.pool:
|
||||
return b
|
||||
default:
|
||||
return newStatsdBuffer(p.bufferMaxSize, p.bufferMaxElements)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bufferPool) returnBuffer(buffer *statsdBuffer) {
|
||||
buffer.reset()
|
||||
select {
|
||||
case p.pool <- buffer:
|
||||
default:
|
||||
}
|
||||
}
|
||||
82
vendor/github.com/DataDog/datadog-go/v5/statsd/buffered_metric_context.go
generated
vendored
Normal file
82
vendor/github.com/DataDog/datadog-go/v5/statsd/buffered_metric_context.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// bufferedMetricContexts represent the contexts for Histograms, Distributions
|
||||
// and Timing. Since those 3 metric types behave the same way and are sampled
|
||||
// with the same type they're represented by the same class.
|
||||
type bufferedMetricContexts struct {
|
||||
nbContext uint64
|
||||
mutex sync.RWMutex
|
||||
values bufferedMetricMap
|
||||
newMetric func(string, float64, string) *bufferedMetric
|
||||
|
||||
// Each bufferedMetricContexts uses its own random source and random
|
||||
// lock to prevent goroutines from contending for the lock on the
|
||||
// "math/rand" package-global random source (e.g. calls like
|
||||
// "rand.Float64()" must acquire a shared lock to get the next
|
||||
// pseudorandom number).
|
||||
random *rand.Rand
|
||||
randomLock sync.Mutex
|
||||
}
|
||||
|
||||
func newBufferedContexts(newMetric func(string, float64, string) *bufferedMetric) bufferedMetricContexts {
|
||||
return bufferedMetricContexts{
|
||||
values: bufferedMetricMap{},
|
||||
newMetric: newMetric,
|
||||
// Note that calling "time.Now().UnixNano()" repeatedly quickly may return
|
||||
// very similar values. That's fine for seeding the worker-specific random
|
||||
// source because we just need an evenly distributed stream of float values.
|
||||
// Do not use this random source for cryptographic randomness.
|
||||
random: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
}
|
||||
}
|
||||
|
||||
func (bc *bufferedMetricContexts) flush(metrics []metric) []metric {
|
||||
bc.mutex.Lock()
|
||||
values := bc.values
|
||||
bc.values = bufferedMetricMap{}
|
||||
bc.mutex.Unlock()
|
||||
|
||||
for _, d := range values {
|
||||
metrics = append(metrics, d.flushUnsafe())
|
||||
}
|
||||
atomic.AddUint64(&bc.nbContext, uint64(len(values)))
|
||||
return metrics
|
||||
}
|
||||
|
||||
func (bc *bufferedMetricContexts) sample(name string, value float64, tags []string, rate float64) error {
|
||||
if !shouldSample(rate, bc.random, &bc.randomLock) {
|
||||
return nil
|
||||
}
|
||||
|
||||
context, stringTags := getContextAndTags(name, tags)
|
||||
|
||||
bc.mutex.RLock()
|
||||
if v, found := bc.values[context]; found {
|
||||
v.sample(value)
|
||||
bc.mutex.RUnlock()
|
||||
return nil
|
||||
}
|
||||
bc.mutex.RUnlock()
|
||||
|
||||
bc.mutex.Lock()
|
||||
// Check if another goroutines hasn't created the value betwen the 'RUnlock' and 'Lock'
|
||||
if v, found := bc.values[context]; found {
|
||||
v.sample(value)
|
||||
bc.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
bc.values[context] = bc.newMetric(name, value, stringTags)
|
||||
bc.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bc *bufferedMetricContexts) getNbContext() uint64 {
|
||||
return atomic.LoadUint64(&bc.nbContext)
|
||||
}
|
||||
75
vendor/github.com/DataDog/datadog-go/v5/statsd/event.go
generated
vendored
Normal file
75
vendor/github.com/DataDog/datadog-go/v5/statsd/event.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Events support
|
||||
// EventAlertType and EventAlertPriority became exported types after this issue was submitted: https://github.com/DataDog/datadog-go/issues/41
|
||||
// The reason why they got exported is so that client code can directly use the types.
|
||||
|
||||
// EventAlertType is the alert type for events
|
||||
type EventAlertType string
|
||||
|
||||
const (
|
||||
// Info is the "info" AlertType for events
|
||||
Info EventAlertType = "info"
|
||||
// Error is the "error" AlertType for events
|
||||
Error EventAlertType = "error"
|
||||
// Warning is the "warning" AlertType for events
|
||||
Warning EventAlertType = "warning"
|
||||
// Success is the "success" AlertType for events
|
||||
Success EventAlertType = "success"
|
||||
)
|
||||
|
||||
// EventPriority is the event priority for events
|
||||
type EventPriority string
|
||||
|
||||
const (
|
||||
// Normal is the "normal" Priority for events
|
||||
Normal EventPriority = "normal"
|
||||
// Low is the "low" Priority for events
|
||||
Low EventPriority = "low"
|
||||
)
|
||||
|
||||
// An Event is an object that can be posted to your DataDog event stream.
|
||||
type Event struct {
|
||||
// Title of the event. Required.
|
||||
Title string
|
||||
// Text is the description of the event. Required.
|
||||
Text string
|
||||
// Timestamp is a timestamp for the event. If not provided, the dogstatsd
|
||||
// server will set this to the current time.
|
||||
Timestamp time.Time
|
||||
// Hostname for the event.
|
||||
Hostname string
|
||||
// AggregationKey groups this event with others of the same key.
|
||||
AggregationKey string
|
||||
// Priority of the event. Can be statsd.Low or statsd.Normal.
|
||||
Priority EventPriority
|
||||
// SourceTypeName is a source type for the event.
|
||||
SourceTypeName string
|
||||
// AlertType can be statsd.Info, statsd.Error, statsd.Warning, or statsd.Success.
|
||||
// If absent, the default value applied by the dogstatsd server is Info.
|
||||
AlertType EventAlertType
|
||||
// Tags for the event.
|
||||
Tags []string
|
||||
}
|
||||
|
||||
// NewEvent creates a new event with the given title and text. Error checking
|
||||
// against these values is done at send-time, or upon running e.Check.
|
||||
func NewEvent(title, text string) *Event {
|
||||
return &Event{
|
||||
Title: title,
|
||||
Text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Check verifies that an event is valid.
|
||||
func (e *Event) Check() error {
|
||||
if len(e.Title) == 0 {
|
||||
return fmt.Errorf("statsd.Event title is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
39
vendor/github.com/DataDog/datadog-go/v5/statsd/fnv1a.go
generated
vendored
Normal file
39
vendor/github.com/DataDog/datadog-go/v5/statsd/fnv1a.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package statsd
|
||||
|
||||
const (
|
||||
// FNV-1a
|
||||
offset32 = uint32(2166136261)
|
||||
prime32 = uint32(16777619)
|
||||
|
||||
// init32 is what 32 bits hash values should be initialized with.
|
||||
init32 = offset32
|
||||
)
|
||||
|
||||
// HashString32 returns the hash of s.
|
||||
func hashString32(s string) uint32 {
|
||||
return addString32(init32, s)
|
||||
}
|
||||
|
||||
// AddString32 adds the hash of s to the precomputed hash value h.
|
||||
func addString32(h uint32, s string) uint32 {
|
||||
i := 0
|
||||
n := (len(s) / 8) * 8
|
||||
|
||||
for i != n {
|
||||
h = (h ^ uint32(s[i])) * prime32
|
||||
h = (h ^ uint32(s[i+1])) * prime32
|
||||
h = (h ^ uint32(s[i+2])) * prime32
|
||||
h = (h ^ uint32(s[i+3])) * prime32
|
||||
h = (h ^ uint32(s[i+4])) * prime32
|
||||
h = (h ^ uint32(s[i+5])) * prime32
|
||||
h = (h ^ uint32(s[i+6])) * prime32
|
||||
h = (h ^ uint32(s[i+7])) * prime32
|
||||
i += 8
|
||||
}
|
||||
|
||||
for _, c := range s[i:] {
|
||||
h = (h ^ uint32(c)) * prime32
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
257
vendor/github.com/DataDog/datadog-go/v5/statsd/format.go
generated
vendored
Normal file
257
vendor/github.com/DataDog/datadog-go/v5/statsd/format.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
gaugeSymbol = []byte("g")
|
||||
countSymbol = []byte("c")
|
||||
histogramSymbol = []byte("h")
|
||||
distributionSymbol = []byte("d")
|
||||
setSymbol = []byte("s")
|
||||
timingSymbol = []byte("ms")
|
||||
tagSeparatorSymbol = ","
|
||||
)
|
||||
|
||||
func appendHeader(buffer []byte, namespace string, name string) []byte {
|
||||
if namespace != "" {
|
||||
buffer = append(buffer, namespace...)
|
||||
}
|
||||
buffer = append(buffer, name...)
|
||||
buffer = append(buffer, ':')
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendRate(buffer []byte, rate float64) []byte {
|
||||
if rate < 1 {
|
||||
buffer = append(buffer, "|@"...)
|
||||
buffer = strconv.AppendFloat(buffer, rate, 'f', -1, 64)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendWithoutNewlines(buffer []byte, s string) []byte {
|
||||
// fastpath for strings without newlines
|
||||
if strings.IndexByte(s, '\n') == -1 {
|
||||
return append(buffer, s...)
|
||||
}
|
||||
|
||||
for _, b := range []byte(s) {
|
||||
if b != '\n' {
|
||||
buffer = append(buffer, b)
|
||||
}
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendTags(buffer []byte, globalTags []string, tags []string) []byte {
|
||||
if len(globalTags) == 0 && len(tags) == 0 {
|
||||
return buffer
|
||||
}
|
||||
buffer = append(buffer, "|#"...)
|
||||
firstTag := true
|
||||
|
||||
for _, tag := range globalTags {
|
||||
if !firstTag {
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
}
|
||||
buffer = appendWithoutNewlines(buffer, tag)
|
||||
firstTag = false
|
||||
}
|
||||
for _, tag := range tags {
|
||||
if !firstTag {
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
}
|
||||
buffer = appendWithoutNewlines(buffer, tag)
|
||||
firstTag = false
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendTagsAggregated(buffer []byte, globalTags []string, tags string) []byte {
|
||||
if len(globalTags) == 0 && tags == "" {
|
||||
return buffer
|
||||
}
|
||||
|
||||
buffer = append(buffer, "|#"...)
|
||||
firstTag := true
|
||||
|
||||
for _, tag := range globalTags {
|
||||
if !firstTag {
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
}
|
||||
buffer = appendWithoutNewlines(buffer, tag)
|
||||
firstTag = false
|
||||
}
|
||||
if tags != "" {
|
||||
if !firstTag {
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
}
|
||||
buffer = appendWithoutNewlines(buffer, tags)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendFloatMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64, precision int) []byte {
|
||||
buffer = appendHeader(buffer, namespace, name)
|
||||
buffer = strconv.AppendFloat(buffer, value, 'f', precision, 64)
|
||||
buffer = append(buffer, '|')
|
||||
buffer = append(buffer, typeSymbol...)
|
||||
buffer = appendRate(buffer, rate)
|
||||
buffer = appendTags(buffer, globalTags, tags)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendIntegerMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value int64, tags []string, rate float64) []byte {
|
||||
buffer = appendHeader(buffer, namespace, name)
|
||||
buffer = strconv.AppendInt(buffer, value, 10)
|
||||
buffer = append(buffer, '|')
|
||||
buffer = append(buffer, typeSymbol...)
|
||||
buffer = appendRate(buffer, rate)
|
||||
buffer = appendTags(buffer, globalTags, tags)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendStringMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value string, tags []string, rate float64) []byte {
|
||||
buffer = appendHeader(buffer, namespace, name)
|
||||
buffer = append(buffer, value...)
|
||||
buffer = append(buffer, '|')
|
||||
buffer = append(buffer, typeSymbol...)
|
||||
buffer = appendRate(buffer, rate)
|
||||
buffer = appendTags(buffer, globalTags, tags)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendGauge(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte {
|
||||
return appendFloatMetric(buffer, gaugeSymbol, namespace, globalTags, name, value, tags, rate, -1)
|
||||
}
|
||||
|
||||
func appendCount(buffer []byte, namespace string, globalTags []string, name string, value int64, tags []string, rate float64) []byte {
|
||||
return appendIntegerMetric(buffer, countSymbol, namespace, globalTags, name, value, tags, rate)
|
||||
}
|
||||
|
||||
func appendHistogram(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte {
|
||||
return appendFloatMetric(buffer, histogramSymbol, namespace, globalTags, name, value, tags, rate, -1)
|
||||
}
|
||||
|
||||
func appendDistribution(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte {
|
||||
return appendFloatMetric(buffer, distributionSymbol, namespace, globalTags, name, value, tags, rate, -1)
|
||||
}
|
||||
|
||||
func appendSet(buffer []byte, namespace string, globalTags []string, name string, value string, tags []string, rate float64) []byte {
|
||||
return appendStringMetric(buffer, setSymbol, namespace, globalTags, name, value, tags, rate)
|
||||
}
|
||||
|
||||
func appendTiming(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte {
|
||||
return appendFloatMetric(buffer, timingSymbol, namespace, globalTags, name, value, tags, rate, 6)
|
||||
}
|
||||
|
||||
func escapedEventTextLen(text string) int {
|
||||
return len(text) + strings.Count(text, "\n")
|
||||
}
|
||||
|
||||
func appendEscapedEventText(buffer []byte, text string) []byte {
|
||||
for _, b := range []byte(text) {
|
||||
if b != '\n' {
|
||||
buffer = append(buffer, b)
|
||||
} else {
|
||||
buffer = append(buffer, "\\n"...)
|
||||
}
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendEvent(buffer []byte, event *Event, globalTags []string) []byte {
|
||||
escapedTextLen := escapedEventTextLen(event.Text)
|
||||
|
||||
buffer = append(buffer, "_e{"...)
|
||||
buffer = strconv.AppendInt(buffer, int64(len(event.Title)), 10)
|
||||
buffer = append(buffer, tagSeparatorSymbol...)
|
||||
buffer = strconv.AppendInt(buffer, int64(escapedTextLen), 10)
|
||||
buffer = append(buffer, "}:"...)
|
||||
buffer = append(buffer, event.Title...)
|
||||
buffer = append(buffer, '|')
|
||||
if escapedTextLen != len(event.Text) {
|
||||
buffer = appendEscapedEventText(buffer, event.Text)
|
||||
} else {
|
||||
buffer = append(buffer, event.Text...)
|
||||
}
|
||||
|
||||
if !event.Timestamp.IsZero() {
|
||||
buffer = append(buffer, "|d:"...)
|
||||
buffer = strconv.AppendInt(buffer, int64(event.Timestamp.Unix()), 10)
|
||||
}
|
||||
|
||||
if len(event.Hostname) != 0 {
|
||||
buffer = append(buffer, "|h:"...)
|
||||
buffer = append(buffer, event.Hostname...)
|
||||
}
|
||||
|
||||
if len(event.AggregationKey) != 0 {
|
||||
buffer = append(buffer, "|k:"...)
|
||||
buffer = append(buffer, event.AggregationKey...)
|
||||
}
|
||||
|
||||
if len(event.Priority) != 0 {
|
||||
buffer = append(buffer, "|p:"...)
|
||||
buffer = append(buffer, event.Priority...)
|
||||
}
|
||||
|
||||
if len(event.SourceTypeName) != 0 {
|
||||
buffer = append(buffer, "|s:"...)
|
||||
buffer = append(buffer, event.SourceTypeName...)
|
||||
}
|
||||
|
||||
if len(event.AlertType) != 0 {
|
||||
buffer = append(buffer, "|t:"...)
|
||||
buffer = append(buffer, string(event.AlertType)...)
|
||||
}
|
||||
|
||||
buffer = appendTags(buffer, globalTags, event.Tags)
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendEscapedServiceCheckText(buffer []byte, text string) []byte {
|
||||
for i := 0; i < len(text); i++ {
|
||||
if text[i] == '\n' {
|
||||
buffer = append(buffer, "\\n"...)
|
||||
} else if text[i] == 'm' && i+1 < len(text) && text[i+1] == ':' {
|
||||
buffer = append(buffer, "m\\:"...)
|
||||
i++
|
||||
} else {
|
||||
buffer = append(buffer, text[i])
|
||||
}
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendServiceCheck(buffer []byte, serviceCheck *ServiceCheck, globalTags []string) []byte {
|
||||
buffer = append(buffer, "_sc|"...)
|
||||
buffer = append(buffer, serviceCheck.Name...)
|
||||
buffer = append(buffer, '|')
|
||||
buffer = strconv.AppendInt(buffer, int64(serviceCheck.Status), 10)
|
||||
|
||||
if !serviceCheck.Timestamp.IsZero() {
|
||||
buffer = append(buffer, "|d:"...)
|
||||
buffer = strconv.AppendInt(buffer, int64(serviceCheck.Timestamp.Unix()), 10)
|
||||
}
|
||||
|
||||
if len(serviceCheck.Hostname) != 0 {
|
||||
buffer = append(buffer, "|h:"...)
|
||||
buffer = append(buffer, serviceCheck.Hostname...)
|
||||
}
|
||||
|
||||
buffer = appendTags(buffer, globalTags, serviceCheck.Tags)
|
||||
|
||||
if len(serviceCheck.Message) != 0 {
|
||||
buffer = append(buffer, "|m:"...)
|
||||
buffer = appendEscapedServiceCheckText(buffer, serviceCheck.Message)
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
func appendSeparator(buffer []byte) []byte {
|
||||
return append(buffer, '\n')
|
||||
}
|
||||
181
vendor/github.com/DataDog/datadog-go/v5/statsd/metrics.go
generated
vendored
Normal file
181
vendor/github.com/DataDog/datadog-go/v5/statsd/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
package statsd
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
/*
|
||||
Those are metrics type that can be aggregated on the client side:
|
||||
- Gauge
|
||||
- Count
|
||||
- Set
|
||||
*/
|
||||
|
||||
type countMetric struct {
|
||||
value int64
|
||||
name string
|
||||
tags []string
|
||||
}
|
||||
|
||||
func newCountMetric(name string, value int64, tags []string) *countMetric {
|
||||
return &countMetric{
|
||||
value: value,
|
||||
name: name,
|
||||
tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *countMetric) sample(v int64) {
|
||||
atomic.AddInt64(&c.value, v)
|
||||
}
|
||||
|
||||
func (c *countMetric) flushUnsafe() metric {
|
||||
return metric{
|
||||
metricType: count,
|
||||
name: c.name,
|
||||
tags: c.tags,
|
||||
rate: 1,
|
||||
ivalue: c.value,
|
||||
}
|
||||
}
|
||||
|
||||
// Gauge
|
||||
|
||||
type gaugeMetric struct {
|
||||
value uint64
|
||||
name string
|
||||
tags []string
|
||||
}
|
||||
|
||||
func newGaugeMetric(name string, value float64, tags []string) *gaugeMetric {
|
||||
return &gaugeMetric{
|
||||
value: math.Float64bits(value),
|
||||
name: name,
|
||||
tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gaugeMetric) sample(v float64) {
|
||||
atomic.StoreUint64(&g.value, math.Float64bits(v))
|
||||
}
|
||||
|
||||
func (g *gaugeMetric) flushUnsafe() metric {
|
||||
return metric{
|
||||
metricType: gauge,
|
||||
name: g.name,
|
||||
tags: g.tags,
|
||||
rate: 1,
|
||||
fvalue: math.Float64frombits(g.value),
|
||||
}
|
||||
}
|
||||
|
||||
// Set
|
||||
|
||||
type setMetric struct {
|
||||
data map[string]struct{}
|
||||
name string
|
||||
tags []string
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func newSetMetric(name string, value string, tags []string) *setMetric {
|
||||
set := &setMetric{
|
||||
data: map[string]struct{}{},
|
||||
name: name,
|
||||
tags: tags,
|
||||
}
|
||||
set.data[value] = struct{}{}
|
||||
return set
|
||||
}
|
||||
|
||||
func (s *setMetric) sample(v string) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.data[v] = struct{}{}
|
||||
}
|
||||
|
||||
// Sets are aggregated on the agent side too. We flush the keys so a set from
|
||||
// multiple application can be correctly aggregated on the agent side.
|
||||
func (s *setMetric) flushUnsafe() []metric {
|
||||
if len(s.data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
metrics := make([]metric, len(s.data))
|
||||
i := 0
|
||||
for value := range s.data {
|
||||
metrics[i] = metric{
|
||||
metricType: set,
|
||||
name: s.name,
|
||||
tags: s.tags,
|
||||
rate: 1,
|
||||
svalue: value,
|
||||
}
|
||||
i++
|
||||
}
|
||||
return metrics
|
||||
}
|
||||
|
||||
// Histograms, Distributions and Timings
|
||||
|
||||
type bufferedMetric struct {
|
||||
sync.Mutex
|
||||
|
||||
data []float64
|
||||
name string
|
||||
// Histograms and Distributions store tags as one string since we need
|
||||
// to compute its size multiple time when serializing.
|
||||
tags string
|
||||
mtype metricType
|
||||
}
|
||||
|
||||
func (s *bufferedMetric) sample(v float64) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.data = append(s.data, v)
|
||||
}
|
||||
|
||||
func (s *bufferedMetric) flushUnsafe() metric {
|
||||
return metric{
|
||||
metricType: s.mtype,
|
||||
name: s.name,
|
||||
stags: s.tags,
|
||||
rate: 1,
|
||||
fvalues: s.data,
|
||||
}
|
||||
}
|
||||
|
||||
type histogramMetric = bufferedMetric
|
||||
|
||||
func newHistogramMetric(name string, value float64, stringTags string) *histogramMetric {
|
||||
return &histogramMetric{
|
||||
data: []float64{value},
|
||||
name: name,
|
||||
tags: stringTags,
|
||||
mtype: histogramAggregated,
|
||||
}
|
||||
}
|
||||
|
||||
type distributionMetric = bufferedMetric
|
||||
|
||||
func newDistributionMetric(name string, value float64, stringTags string) *distributionMetric {
|
||||
return &distributionMetric{
|
||||
data: []float64{value},
|
||||
name: name,
|
||||
tags: stringTags,
|
||||
mtype: distributionAggregated,
|
||||
}
|
||||
}
|
||||
|
||||
type timingMetric = bufferedMetric
|
||||
|
||||
func newTimingMetric(name string, value float64, stringTags string) *timingMetric {
|
||||
return &timingMetric{
|
||||
data: []float64{value},
|
||||
name: name,
|
||||
tags: stringTags,
|
||||
mtype: timingAggregated,
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user