mirror of https://github.com/knative/caching.git
Pin test-infra & pkg to release-0.16 (#281)
This commit is contained in:
parent
787aec80f7
commit
a78409990d
4
go.mod
4
go.mod
|
@ -16,8 +16,8 @@ require (
|
|||
k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
|
||||
k8s.io/code-generator v0.18.0
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
|
||||
knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7
|
||||
knative.dev/test-infra v0.0.0-20200519161858-554a95a37986
|
||||
knative.dev/pkg v0.0.0-20200630170034-2c1a029eb97f
|
||||
knative.dev/test-infra v0.0.0-20200630141629-15f40fe97047
|
||||
)
|
||||
|
||||
replace (
|
||||
|
|
186
go.sum
186
go.sum
|
@ -1,11 +1,10 @@
|
|||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
cloud.google.com/go v0.25.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.30.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
|
||||
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
|
||||
cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
|
@ -46,29 +45,20 @@ contrib.go.opencensus.io/exporter/zipkin v0.1.1/go.mod h1:GMvdSl3eJ2gapOaLKzTKE3
|
|||
contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
|
||||
contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuEd+YuKoUiazDC/N96FiDEU=
|
||||
github.com/Azure/azure-pipeline-go v0.1.8/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
|
||||
github.com/Azure/azure-pipeline-go v0.1.9/go.mod h1:XA1kFWRVhSK+KNFiOhfv83Fv8L9achrP7OxIzeTn1Yg=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v19.1.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v21.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v28.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0=
|
||||
github.com/Azure/azure-storage-blob-go v0.0.0-20190123011202-457680cc0804/go.mod h1:oGfmITT1V6x//CswqY2gtAHND+xIP64/qL7a5QJix0Y=
|
||||
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest v10.15.5+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
|
||||
github.com/Azure/go-autorest/autorest v0.2.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg=
|
||||
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/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||
|
@ -82,11 +72,9 @@ github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+v
|
|||
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/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/autorest/to v0.1.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
|
||||
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
|
@ -98,7 +86,6 @@ github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t
|
|||
github.com/GoogleCloudPlatform/cloud-builders/gcs-fetcher v0.0.0-20191203181535-308b93ad1f39/go.mod h1:yfGmCjKuUzk9WzubMlW2zwjhCraIc/J+M40cufdemRM=
|
||||
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo=
|
||||
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
|
||||
github.com/GoogleCloudPlatform/testgrid v0.0.1-alpha.3/go.mod h1:f96W2HYy3tiBNV5zbbRc+NczwYHgG1PHXMQfoEWv680=
|
||||
github.com/GoogleCloudPlatform/testgrid v0.0.7/go.mod h1:lmtHGBL0M/MLbu1tR9BWV7FGZ1FEFIdPqmJiHNCL7y8=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
|
@ -138,22 +125,18 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY
|
|||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/aws/aws-k8s-tester v0.0.0-20190114231546-b411acf57dfe/go.mod h1:1ADF5tAtU1/mVtfMcHAYSm2fPw71DA7fFk0yed64/0I=
|
||||
github.com/aws/aws-k8s-tester v0.9.3/go.mod h1:nsh1f7joi8ZI1lvR+Ron6kJM2QdCYPU/vFePghSSuTc=
|
||||
github.com/aws/aws-k8s-tester v1.0.0/go.mod h1:NUNd9k43+h9O5tvwL+4N1Ctb//SapmeeFX1G0/2/0Qc=
|
||||
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||
github.com/aws/aws-sdk-go v1.15.90/go.mod h1:es1KtYUFs7le0xQ3rOihkuoVD90z7D0fR2Qm4S00/gU=
|
||||
github.com/aws/aws-sdk-go v1.16.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.23.20 h1:2CBuL21P0yKdZN5urf2NxKa1ha8fhnY+A3pBCHFeZoA=
|
||||
github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.23.22/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.29.32/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
|
||||
github.com/aws/aws-sdk-go v1.29.34 h1:yrzwfDaZFe9oT4AmQeNNunSQA7c0m2chz0B43+bJ1ok=
|
||||
github.com/aws/aws-sdk-go v1.29.34/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg=
|
||||
github.com/aws/aws-sdk-go v1.30.4/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.30.5 h1:i+sSesaMrSxiUt3NJddOApe2mXK+VNBgfcmRTvNFrXM=
|
||||
github.com/aws/aws-sdk-go v1.30.5/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/bazelbuild/buildtools v0.0.0-20190917191645-69366ca98f89/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU=
|
||||
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
|
@ -190,7 +173,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
|||
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo=
|
||||
github.com/clarketm/json v1.13.4/go.mod h1:ynr2LRfb0fQU34l07csRNBTcivjySLLiY1YzQqKVfdo=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudevents/sdk-go v0.0.0-20190509003705-56931988abe3/go.mod h1:j1nZWMLGg3om8SswStBoY6/SHvcLM19MuZqwDtMtmzs=
|
||||
github.com/cloudevents/sdk-go v1.0.0/go.mod h1:3TkmM0cFqkhCHOq5JzzRU/RxRkwzoS8TZ+G448qVTog=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||
|
@ -225,7 +208,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190111225525-2fea367d496d/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
|
||||
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
|
||||
|
@ -239,14 +221,11 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
|
|||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/djherbis/atime v1.0.0/go.mod h1:5W+KBIuTwVGcqjIfaTwt+KSYX1o6uep8dtevevQP/f8=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/cli v0.0.0-20190925022749-754388324470/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||
github.com/docker/distribution v2.6.0-rc.1.0.20180327202408-83389a148052+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v1.4.2-0.20180531152204-71cd53e4a197/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
|
@ -297,11 +276,12 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
|||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ini/ini v1.46.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ini/ini v1.55.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
|
@ -366,20 +346,16 @@ github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfT
|
|||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||
github.com/go-sql-driver/mysql v0.0.0-20160411075031-7ebe0a500653/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
|
||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
|
@ -400,7 +376,6 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er
|
|||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
|
@ -421,7 +396,6 @@ github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAO
|
|||
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
|
||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
||||
github.com/gomodule/redigo v1.7.0/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
@ -429,7 +403,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-containerregistry v0.0.0-20191010200024-a3d713f9b7f8/go.mod h1:KyKXa9ciM8+lgMXwOVsXi7UxGrsf9mM61Mzs+xKUrKE=
|
||||
github.com/google/go-containerregistry v0.0.0-20200115214256-379933c9c22b/go.mod h1:Wtl/v6YdQxv397EREtzwgd9+Ud7Q5D8XMbi3Zazgkrs=
|
||||
github.com/google/go-containerregistry v0.0.0-20200123184029-53ce695e4179/go.mod h1:Wtl/v6YdQxv397EREtzwgd9+Ud7Q5D8XMbi3Zazgkrs=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
|
@ -438,7 +411,6 @@ github.com/google/go-licenses v0.0.0-20191112164736-212ea350c932/go.mod h1:16wa6
|
|||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
|
||||
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
|
@ -458,12 +430,9 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
|
|||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww=
|
||||
github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
|
@ -484,18 +453,15 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
|
|||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
|
||||
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.4.1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
|
@ -507,7 +473,6 @@ github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1
|
|||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20171204182908-b7773ae21874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
|
@ -536,11 +501,9 @@ github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQT
|
|||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jenkins-x/go-scm v1.5.65/go.mod h1:MgGRkJScE/rJ30J/bXYqduN5sDPZqZFITJopsnZmTOw=
|
||||
github.com/jenkins-x/go-scm v1.5.79/go.mod h1:PCT338UhP/pQ0IeEeMEf/hoLTYKcH7qjGEKd7jPkeYg=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/gorm v0.0.0-20170316141641-572d0a0ab1eb/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
|
||||
github.com/jinzhu/inflection v0.0.0-20190603042836-f5c5f50e6090/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
|
@ -553,8 +516,6 @@ github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeY
|
|||
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
|
@ -567,7 +528,7 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
|||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
|
@ -577,10 +538,7 @@ github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
|
|||
github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/knative/build v0.1.2/go.mod h1:/sU74ZQkwlYA5FwYDJhYTy61i/Kn+5eWfln2jDbw3Qo=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
|
@ -588,13 +546,11 @@ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.0.0/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
|
@ -623,10 +579,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
|
|||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v0.0.0-20160514122348-38ee283dabf1/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.0/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
|
||||
|
@ -652,10 +606,13 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
|
|||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/nats-io/gnatsd v1.4.1/go.mod h1:nqco77VO78hLCJpIcVfygDP2rPGfsEHkGTUk94uh5DQ=
|
||||
github.com/nats-io/go-nats v1.7.0/go.mod h1:+t7RHT5ApZebkrQdnn6AhQJmhJJiKAvJUio1PiiCtj0=
|
||||
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
|
||||
github.com/nats-io/nuid v1.0.0/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
|
||||
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
|
@ -671,7 +628,6 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
|||
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
|
@ -688,7 +644,6 @@ github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5X
|
|||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.0/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2 h1:nY8Hti+WKaP0cRsSeQ026wU03QsM762XBeCXBb9NAWI=
|
||||
|
@ -699,7 +654,6 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
|
|||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.3.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
|
@ -718,8 +672,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
|
@ -729,7 +681,6 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
|
|||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A=
|
||||
github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_model v0.0.0-20170216185247-6f3806018612/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
|
@ -739,9 +690,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
|||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
|
@ -753,8 +701,6 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2
|
|||
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20180612222113-7d6f385de8be/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
|
@ -772,29 +718,22 @@ github.com/rcrowley/go-metrics v0.0.0-20190706150252-9beb055b7962/go.mod h1:bCqn
|
|||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v0.0.0-20160713180306-0aa62d5ddceb/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20180925043049-51d7b505e2e9/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20190718010115-4ba037080260/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20191102174205-af46314aec7b/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/graphql v0.0.0-20180924043259-e4a3a37e6d42/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
|
||||
github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.1.1/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
|
@ -817,7 +756,6 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0
|
|||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
|
@ -840,9 +778,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
|
|||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tektoncd/pipeline v0.8.0/go.mod h1:IZzJdiX9EqEMuUcgdnElozdYYRh0/ZRC+NKMLj1K3Yw=
|
||||
github.com/tektoncd/pipeline v0.10.1/go.mod h1:D2X0exT46zYx95BU7ByM8+erpjoN7thmUBvlKThOszU=
|
||||
github.com/tektoncd/plumbing v0.0.0-20191216083742-847dcf196de9/go.mod h1:QZHgU07PRBTRF6N57w4+ApRu8OgfYLFNqCDlfEZaD9Y=
|
||||
github.com/tektoncd/pipeline v0.11.0/go.mod h1:hlkH32S92+/UODROH0dmxzyuMxfRFp/Nc3e29MewLn8=
|
||||
github.com/tektoncd/plumbing v0.0.0-20200217163359-cd0db6e567d2/go.mod h1:QZHgU07PRBTRF6N57w4+ApRu8OgfYLFNqCDlfEZaD9Y=
|
||||
github.com/tektoncd/plumbing/pipelinerun-logs v0.0.0-20191206114338-712d544c2c21/go.mod h1:S62EUWtqmejjJgUMOGB1CCCHRp6C706laH06BoALkzU=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
|
@ -851,13 +788,12 @@ github.com/tsenart/go-tsz v0.0.0-20180814235614-0bd30b3df1c3 h1:pcQGQzTwCg//7FgV
|
|||
github.com/tsenart/go-tsz v0.0.0-20180814235614-0bd30b3df1c3/go.mod h1:SWZznP1z5Ki7hDT2ioqiFKEse8K9tU2OUvaRI0NeGQo=
|
||||
github.com/tsenart/vegeta v12.7.1-0.20190725001342-b5f4fca92137+incompatible h1:ErZrHhRveAoznVW80gbrxz+qxJNydpA2fcQxTPHkZbU=
|
||||
github.com/tsenart/vegeta v12.7.1-0.20190725001342-b5f4fca92137+incompatible/go.mod h1:Smz/ZWfhKRcyDDChZkG3CyTHdj87lHzio/HOCkbndXM=
|
||||
github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/vdemeester/k8s-pkg-credentialprovider v0.0.0-20200107171650-7c61ffa44238/go.mod h1:JwQJCMWpUDqjZrB5jpw0f5VbN7U95zxFy1ZDpoEarGo=
|
||||
github.com/vdemeester/k8s-pkg-credentialprovider v1.13.12-1/go.mod h1:Fko0rTxEtDW2kju5Ky7yFJNS3IcNvW8IPsp4/e9oev0=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
|
@ -870,7 +806,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
|
|||
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
|
@ -878,16 +813,13 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
go.etcd.io/bbolt v1.3.1-etcd.7/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20181031231232-83304cfc808c/go.mod h1:weASp41xM3dk0YHg1s/W8ecdGP5G4teSTMBPpYAaUgA=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
|
||||
go.opencensus.io v0.17.0/go.mod h1:mp1VrMQxhlqqDpKvH4UcQUa4YwlzNmymAjPrDdfxNpI=
|
||||
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=
|
||||
|
@ -897,28 +829,31 @@ go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
|
|||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.9.2-0.20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo=
|
||||
go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
|
||||
gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180608092829-8ac0e0d97ce4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
@ -961,7 +896,6 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN
|
|||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -1012,7 +946,6 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0 h1:2mqDk8w/o6UmeUCu5Qiq2y7iMf6anbx+YA8d1JFoFrs=
|
||||
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -1021,9 +954,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
|
@ -1050,7 +981,6 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190219203350-90b0e4468f99/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1071,7 +1001,6 @@ golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8 h1:41hwlulw1prEMBxLQSlMSux1zxJf07B3WPsdjJlKZxE=
|
||||
golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1082,7 +1011,6 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -1159,6 +1087,7 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
|
|||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200214144324-88be01311a71/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200303214625-2b0b585e22fe/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
|
@ -1170,6 +1099,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV
|
|||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
|
||||
gomodules.xyz/jsonpatch/v2 v2.1.0 h1:Phva6wqu+xR//Njw6iorylFFgn/z547tw5Ne3HZPQ+k=
|
||||
gomodules.xyz/jsonpatch/v2 v2.1.0/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
|
||||
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
|
||||
|
@ -1178,8 +1109,6 @@ gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6d
|
|||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
|
||||
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181021000519-a2651947f503/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
|
@ -1198,7 +1127,6 @@ google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
|
|||
google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
|
@ -1207,10 +1135,7 @@ google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
|||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
|
||||
google.golang.org/genproto v0.0.0-20180608181217-32ee49c4dd80/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181016170114-94acd270e44e/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
@ -1239,8 +1164,6 @@ google.golang.org/genproto v0.0.0-20200317114155-1f3552e48f24/go.mod h1:55QSHmfG
|
|||
google.golang.org/genproto v0.0.0-20200326112834-f447254575fd h1:DVCc2PgW9UrvHGZGEv4Mt3uSeQtUrrs7r8pUw+bVwWI=
|
||||
google.golang.org/genproto v0.0.0-20200326112834-f447254575fd/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
|
@ -1264,7 +1187,6 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4
|
|||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
|
@ -1303,8 +1225,6 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
@ -1337,7 +1257,6 @@ k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1a
|
|||
k8s.io/component-base v0.17.6/go.mod h1:jgRLWl0B0rOzFNtxQ9E4BphPmDqoMafujdau6AdG2Xo=
|
||||
k8s.io/csi-translation-lib v0.17.0/go.mod h1:HEF7MEz7pOLJCnxabi45IPkhSsE/KmxPQksuCrHKWls=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190306031000-7a1b7fb0289f/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20191108084044-e500ee069b5c/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
|
@ -1345,47 +1264,25 @@ k8s.io/gengo v0.0.0-20200205140755-e0e292d8aa12 h1:pZzawYyz6VRNPVYpqGv61LWCimQv1
|
|||
k8s.io/gengo v0.0.0-20200205140755-e0e292d8aa12/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29 h1:NeQXVJ2XFSkRoPzRo8AId01ZER+j8oV4SZADT4iBOXQ=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU=
|
||||
k8s.io/kubectl v0.17.2/go.mod h1:y4rfLV0n6aPmvbRCqZQjvOp3ezxsFgpqL+zF5jH/lxk=
|
||||
k8s.io/kubernetes v1.11.10/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/kubernetes v1.14.7/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/legacy-cloud-providers v0.17.0/go.mod h1:DdzaepJ3RtRy+e5YhNtrCYwlgyK87j/5+Yfp0L9Syp8=
|
||||
k8s.io/metrics v0.17.2/go.mod h1:3TkNHET4ROd+NfzNxkjoVfQ0Ob4iZnaHmSEA4vYpwLw=
|
||||
k8s.io/test-infra v0.0.0-20181019233642-2e10a0bbe9b3/go.mod h1:2NzXB13Ji0nqpyublHeiPC4FZwU0TknfvyaaNfl/BTA=
|
||||
k8s.io/test-infra v0.0.0-20191212060232-70b0b49fe247/go.mod h1:d8SKryJBXAwfCFVL4wieRez47J2NOOAb9d029sWLseQ=
|
||||
k8s.io/test-infra v0.0.0-20200407001919-bc7f71ef65b8/go.mod h1:/WpJWcaDvuykB322WXP4kJbX8IpalOzuPxA62GpwkJk=
|
||||
k8s.io/utils v0.0.0-20181019225348-5e321f9a457c/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
|
||||
k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20190907131718-3d4f5b7dea0b h1:eMM0sTvh3KBVGwJfuNcU86P38TJhlVMAICbFPDG3t0M=
|
||||
k8s.io/utils v0.0.0-20190907131718-3d4f5b7dea0b/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/test-infra v0.0.0-20200514184223-ba32c8aae783/go.mod h1:bW6thaPZfL2hW7ecjx2WYwlP9KQLM47/xIJyttkVk5s=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20200124190032-861946025e34 h1:HjlUD6M0K3P8nRXmr2B9o4F9dUy9TCj/aEpReeyi6+k=
|
||||
k8s.io/utils v0.0.0-20200124190032-861946025e34/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
knative.dev/caching v0.0.0-20190719140829-2032732871ff/go.mod h1:dHXFU6CGlLlbzaWc32g80cR92iuBSpsslDNBWI8C7eg=
|
||||
knative.dev/eventing-contrib v0.6.1-0.20190723221543-5ce18048c08b/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
|
||||
knative.dev/pkg v0.0.0-20191101194912-56c2594e4f11/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
|
||||
knative.dev/pkg v0.0.0-20191111150521-6d806b998379/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
|
||||
knative.dev/pkg v0.0.0-20200428194351-90fc61bae7f7/go.mod h1:o+e8OVEJKIuvXPsGVPIautjXgs05xbos7G+QMRjuUps=
|
||||
knative.dev/pkg v0.0.0-20200505191044-3da93ebb24c2 h1:Qu2NlOHb9p3g+CSL/ok9+FySowN60URFEKRSXfWtDv4=
|
||||
knative.dev/pkg v0.0.0-20200505191044-3da93ebb24c2/go.mod h1:Q6sL35DdGs8hIQZKdaCXJGgY8f90BmNBKSb8z6d/BTM=
|
||||
knative.dev/pkg v0.0.0-20200515002500-16d7b963416f h1:kcpAMvYUqftHMA69wZ7g83zEW4y8cdnqfdJsSPOlrJQ=
|
||||
knative.dev/pkg v0.0.0-20200515002500-16d7b963416f/go.mod h1:tMOHGbxtRz8zYFGEGpV/bpoTEM1o89MwYFC4YJXl3GY=
|
||||
knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7 h1:9S2r59HZJF9nKvoRLg5zJzx6XpVlVyvVRqz/C/h6h2s=
|
||||
knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7/go.mod h1:QgNZTxnwpB/oSpNcfnLVlw+WpEwwyKAvJlvR3hgeltA=
|
||||
knative.dev/test-infra v0.0.0-20200407185800-1b88cb3b45a5/go.mod h1:xcdUkMJrLlBswIZqL5zCuBFOC22WIPMQoVX1L35i0vQ=
|
||||
knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55 h1:Ajn44+eHHjPQL/BQicj8LMy8VTD2ypMCfHJuZVGEtew=
|
||||
knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55/go.mod h1:WqF1Azka+FxPZ20keR2zCNtiQA1MP9ZB4BH4HuI+SIU=
|
||||
knative.dev/test-infra v0.0.0-20200513011557-d03429a76034 h1:JxqONCZVS7or+Fv3ebVQoipuIBH7Ig3Qbx170hgIF+A=
|
||||
knative.dev/test-infra v0.0.0-20200513011557-d03429a76034/go.mod h1:aMif0KXL4g19YCYwsy4Ocjjz5xgPlseYV+B95Oo4JGE=
|
||||
knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9 h1:kKfV3QWsxugwXsqgjFd72MjZeAHJ381dWqAxH2KMknc=
|
||||
knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9/go.mod h1:A5b2OAXTOeHT3hHhVQm3dmtbuWvIDP7qzgtqxA3/2pE=
|
||||
knative.dev/test-infra v0.0.0-20200519161858-554a95a37986 h1:ZDy43jkWPQ75d4l4DGy+ENQIXlNcnHIh4tB6XxgovNc=
|
||||
knative.dev/test-infra v0.0.0-20200519161858-554a95a37986/go.mod h1:LeNa1Wvn47efeQUkpkn3XG7Fx9Ga+rhAP13SZyjaEGg=
|
||||
knative.dev/caching v0.0.0-20200116200605-67bca2c83dfa/go.mod h1:dHXFU6CGlLlbzaWc32g80cR92iuBSpsslDNBWI8C7eg=
|
||||
knative.dev/eventing-contrib v0.11.2/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
|
||||
knative.dev/pkg v0.0.0-20200207155214-fef852970f43/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
|
||||
knative.dev/pkg v0.0.0-20200630170034-2c1a029eb97f h1:Y3fmAKacNnQvPBK6Onnb0uC87roq/ypS8rYekfIdci8=
|
||||
knative.dev/pkg v0.0.0-20200630170034-2c1a029eb97f/go.mod h1:7T15JzvjKXWnvIKcohz4brrsVq8jvwAcJwWY9xigAc0=
|
||||
knative.dev/test-infra v0.0.0-20200630141629-15f40fe97047 h1:MLcIL0EGkvxRIXcrbPfBRKDVY4wNLVsNbilnZOfHmdk=
|
||||
knative.dev/test-infra v0.0.0-20200630141629-15f40fe97047/go.mod h1:30tMsI1VXrG2m4ut7CFZbLg1VbcRsslPfGU+GWILm6E=
|
||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
|
@ -1398,12 +1295,13 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
|
|||
rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/controller-runtime v0.3.0/go.mod h1:Cw6PkEg0Sa7dAYovGT4R0tRkGhHXpYijwNxYhAnAZZk=
|
||||
sigs.k8s.io/boskos v0.0.0-20200530174753-71e795271860/go.mod h1:L1ubP7d1CCMSQSjKiZv6dGbh7b4kfoG+dFPj8cfYDnI=
|
||||
sigs.k8s.io/controller-runtime v0.5.0/go.mod h1:REiJzC7Y00U+2YkMbT8wxgrsX5USpXKGhb2sCtAXiT8=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff/v2 v2.0.1/go.mod h1:Wb7vfKAodbKgf6tn1Kl0VvGj7mRH6DGaRcixXEJXTsE=
|
||||
sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||
|
|
|
@ -27,7 +27,7 @@ export GO111MODULE=on
|
|||
export GOFLAGS=-mod=vendor
|
||||
|
||||
# This controls the release branch we track.
|
||||
VERSION="release-0.15"
|
||||
VERSION="release-0.16"
|
||||
|
||||
# The list of dependencies that we track at HEAD and periodically
|
||||
# float forward in this repository.
|
||||
|
|
|
@ -107,6 +107,13 @@ type Provider interface {
|
|||
IsExpired() bool
|
||||
}
|
||||
|
||||
// ProviderWithContext is a Provider that can retrieve credentials with a Context
|
||||
type ProviderWithContext interface {
|
||||
Provider
|
||||
|
||||
RetrieveWithContext(Context) (Value, error)
|
||||
}
|
||||
|
||||
// An Expirer is an interface that Providers can implement to expose the expiration
|
||||
// time, if known. If the Provider cannot accurately provide this info,
|
||||
// it should not implement this interface.
|
||||
|
@ -233,7 +240,9 @@ func (c *Credentials) GetWithContext(ctx Context) (Value, error) {
|
|||
// Cannot pass context down to the actual retrieve, because the first
|
||||
// context would cancel the whole group when there is not direct
|
||||
// association of items in the group.
|
||||
resCh := c.sf.DoChan("", c.singleRetrieve)
|
||||
resCh := c.sf.DoChan("", func() (interface{}, error) {
|
||||
return c.singleRetrieve(&suppressedContext{ctx})
|
||||
})
|
||||
select {
|
||||
case res := <-resCh:
|
||||
return res.Val.(Value), res.Err
|
||||
|
@ -243,12 +252,16 @@ func (c *Credentials) GetWithContext(ctx Context) (Value, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Credentials) singleRetrieve() (interface{}, error) {
|
||||
func (c *Credentials) singleRetrieve(ctx Context) (creds interface{}, err error) {
|
||||
if curCreds := c.creds.Load(); !c.isExpired(curCreds) {
|
||||
return curCreds.(Value), nil
|
||||
}
|
||||
|
||||
creds, err := c.provider.Retrieve()
|
||||
if p, ok := c.provider.(ProviderWithContext); ok {
|
||||
creds, err = p.RetrieveWithContext(ctx)
|
||||
} else {
|
||||
creds, err = c.provider.Retrieve()
|
||||
}
|
||||
if err == nil {
|
||||
c.creds.Store(creds)
|
||||
}
|
||||
|
@ -308,3 +321,19 @@ func (c *Credentials) ExpiresAt() (time.Time, error) {
|
|||
}
|
||||
return expirer.ExpiresAt(), nil
|
||||
}
|
||||
|
||||
type suppressedContext struct {
|
||||
Context
|
||||
}
|
||||
|
||||
func (s *suppressedContext) Deadline() (deadline time.Time, ok bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
func (s *suppressedContext) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *suppressedContext) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
|
20
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
20
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
|
@ -7,6 +7,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
|
@ -87,7 +88,14 @@ func NewCredentialsWithClient(client *ec2metadata.EC2Metadata, options ...func(*
|
|||
// Error will be returned if the request fails, or unable to extract
|
||||
// the desired credentials.
|
||||
func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
|
||||
credsList, err := requestCredList(m.Client)
|
||||
return m.RetrieveWithContext(aws.BackgroundContext())
|
||||
}
|
||||
|
||||
// RetrieveWithContext retrieves credentials from the EC2 service.
|
||||
// Error will be returned if the request fails, or unable to extract
|
||||
// the desired credentials.
|
||||
func (m *EC2RoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
|
||||
credsList, err := requestCredList(ctx, m.Client)
|
||||
if err != nil {
|
||||
return credentials.Value{ProviderName: ProviderName}, err
|
||||
}
|
||||
|
@ -97,7 +105,7 @@ func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
|
|||
}
|
||||
credsName := credsList[0]
|
||||
|
||||
roleCreds, err := requestCred(m.Client, credsName)
|
||||
roleCreds, err := requestCred(ctx, m.Client, credsName)
|
||||
if err != nil {
|
||||
return credentials.Value{ProviderName: ProviderName}, err
|
||||
}
|
||||
|
@ -130,8 +138,8 @@ const iamSecurityCredsPath = "iam/security-credentials/"
|
|||
|
||||
// requestCredList requests a list of credentials from the EC2 service.
|
||||
// If there are no credentials, or there is an error making or receiving the request
|
||||
func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
|
||||
resp, err := client.GetMetadata(iamSecurityCredsPath)
|
||||
func requestCredList(ctx aws.Context, client *ec2metadata.EC2Metadata) ([]string, error) {
|
||||
resp, err := client.GetMetadataWithContext(ctx, iamSecurityCredsPath)
|
||||
if err != nil {
|
||||
return nil, awserr.New("EC2RoleRequestError", "no EC2 instance role found", err)
|
||||
}
|
||||
|
@ -154,8 +162,8 @@ func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
|
|||
//
|
||||
// If the credentials cannot be found, or there is an error reading the response
|
||||
// and error will be returned.
|
||||
func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
|
||||
resp, err := client.GetMetadata(sdkuri.PathJoin(iamSecurityCredsPath, credsName))
|
||||
func requestCred(ctx aws.Context, client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
|
||||
resp, err := client.GetMetadataWithContext(ctx, sdkuri.PathJoin(iamSecurityCredsPath, credsName))
|
||||
if err != nil {
|
||||
return ec2RoleCredRespBody{},
|
||||
awserr.New("EC2RoleRequestError",
|
||||
|
|
|
@ -116,7 +116,13 @@ func (p *Provider) IsExpired() bool {
|
|||
// Retrieve will attempt to request the credentials from the endpoint the Provider
|
||||
// was configured for. And error will be returned if the retrieval fails.
|
||||
func (p *Provider) Retrieve() (credentials.Value, error) {
|
||||
resp, err := p.getCredentials()
|
||||
return p.RetrieveWithContext(aws.BackgroundContext())
|
||||
}
|
||||
|
||||
// RetrieveWithContext will attempt to request the credentials from the endpoint the Provider
|
||||
// was configured for. And error will be returned if the retrieval fails.
|
||||
func (p *Provider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
|
||||
resp, err := p.getCredentials(ctx)
|
||||
if err != nil {
|
||||
return credentials.Value{ProviderName: ProviderName},
|
||||
awserr.New("CredentialsEndpointError", "failed to load credentials", err)
|
||||
|
@ -148,7 +154,7 @@ type errorOutput struct {
|
|||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (p *Provider) getCredentials() (*getCredentialsOutput, error) {
|
||||
func (p *Provider) getCredentials(ctx aws.Context) (*getCredentialsOutput, error) {
|
||||
op := &request.Operation{
|
||||
Name: "GetCredentials",
|
||||
HTTPMethod: "GET",
|
||||
|
@ -156,6 +162,7 @@ func (p *Provider) getCredentials() (*getCredentialsOutput, error) {
|
|||
|
||||
out := &getCredentialsOutput{}
|
||||
req := p.Client.NewRequest(op, nil, out)
|
||||
req.SetContext(ctx)
|
||||
req.HTTPRequest.Header.Set("Accept", "application/json")
|
||||
if authToken := p.AuthorizationToken; len(authToken) != 0 {
|
||||
req.HTTPRequest.Header.Set("Authorization", authToken)
|
||||
|
|
20
vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
20
vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go
generated
vendored
|
@ -87,6 +87,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkrand"
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
)
|
||||
|
@ -118,6 +119,10 @@ type AssumeRoler interface {
|
|||
AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
|
||||
}
|
||||
|
||||
type assumeRolerWithContext interface {
|
||||
AssumeRoleWithContext(aws.Context, *sts.AssumeRoleInput, ...request.Option) (*sts.AssumeRoleOutput, error)
|
||||
}
|
||||
|
||||
// DefaultDuration is the default amount of time in minutes that the credentials
|
||||
// will be valid for.
|
||||
var DefaultDuration = time.Duration(15) * time.Minute
|
||||
|
@ -265,6 +270,11 @@ func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*
|
|||
|
||||
// Retrieve generates a new set of temporary credentials using STS.
|
||||
func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
|
||||
return p.RetrieveWithContext(aws.BackgroundContext())
|
||||
}
|
||||
|
||||
// RetrieveWithContext generates a new set of temporary credentials using STS.
|
||||
func (p *AssumeRoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
|
||||
// Apply defaults where parameters are not set.
|
||||
if p.RoleSessionName == "" {
|
||||
// Try to work out a role name that will hopefully end up unique.
|
||||
|
@ -304,7 +314,15 @@ func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
|
|||
}
|
||||
}
|
||||
|
||||
roleOutput, err := p.Client.AssumeRole(input)
|
||||
var roleOutput *sts.AssumeRoleOutput
|
||||
var err error
|
||||
|
||||
if c, ok := p.Client.(assumeRolerWithContext); ok {
|
||||
roleOutput, err = c.AssumeRoleWithContext(ctx, input)
|
||||
} else {
|
||||
roleOutput, err = p.Client.AssumeRole(input)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return credentials.Value{ProviderName: ProviderName}, err
|
||||
}
|
||||
|
|
10
vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/web_identity_provider.go
generated
vendored
10
vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/web_identity_provider.go
generated
vendored
|
@ -64,6 +64,13 @@ func NewWebIdentityRoleProvider(svc stsiface.STSAPI, roleARN, roleSessionName, p
|
|||
// 'WebIdentityTokenFilePath' specified destination and if that is empty an
|
||||
// error will be returned.
|
||||
func (p *WebIdentityRoleProvider) Retrieve() (credentials.Value, error) {
|
||||
return p.RetrieveWithContext(aws.BackgroundContext())
|
||||
}
|
||||
|
||||
// RetrieveWithContext will attempt to assume a role from a token which is located at
|
||||
// 'WebIdentityTokenFilePath' specified destination and if that is empty an
|
||||
// error will be returned.
|
||||
func (p *WebIdentityRoleProvider) RetrieveWithContext(ctx credentials.Context) (credentials.Value, error) {
|
||||
b, err := ioutil.ReadFile(p.tokenFilePath)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("unable to read file at %s", p.tokenFilePath)
|
||||
|
@ -81,6 +88,9 @@ func (p *WebIdentityRoleProvider) Retrieve() (credentials.Value, error) {
|
|||
RoleSessionName: &sessionName,
|
||||
WebIdentityToken: aws.String(string(b)),
|
||||
})
|
||||
|
||||
req.SetContext(ctx)
|
||||
|
||||
// InvalidIdentityToken error is a temporary error that can occur
|
||||
// when assuming an Role with a JWT web identity token.
|
||||
req.RetryErrorCodes = append(req.RetryErrorCodes, sts.ErrCodeInvalidIdentityTokenException)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkuri"
|
||||
|
@ -15,7 +16,7 @@ import (
|
|||
|
||||
// getToken uses the duration to return a token for EC2 metadata service,
|
||||
// or an error if the request failed.
|
||||
func (c *EC2Metadata) getToken(duration time.Duration) (tokenOutput, error) {
|
||||
func (c *EC2Metadata) getToken(ctx aws.Context, duration time.Duration) (tokenOutput, error) {
|
||||
op := &request.Operation{
|
||||
Name: "GetToken",
|
||||
HTTPMethod: "PUT",
|
||||
|
@ -24,6 +25,7 @@ func (c *EC2Metadata) getToken(duration time.Duration) (tokenOutput, error) {
|
|||
|
||||
var output tokenOutput
|
||||
req := c.NewRequest(op, nil, &output)
|
||||
req.SetContext(ctx)
|
||||
|
||||
// remove the fetch token handler from the request handlers to avoid infinite recursion
|
||||
req.Handlers.Sign.RemoveByName(fetchTokenHandlerName)
|
||||
|
@ -50,6 +52,13 @@ func (c *EC2Metadata) getToken(duration time.Duration) (tokenOutput, error) {
|
|||
// instance metadata service. The content will be returned as a string, or
|
||||
// error if the request failed.
|
||||
func (c *EC2Metadata) GetMetadata(p string) (string, error) {
|
||||
return c.GetMetadataWithContext(aws.BackgroundContext(), p)
|
||||
}
|
||||
|
||||
// GetMetadataWithContext uses the path provided to request information from the EC2
|
||||
// instance metadata service. The content will be returned as a string, or
|
||||
// error if the request failed.
|
||||
func (c *EC2Metadata) GetMetadataWithContext(ctx aws.Context, p string) (string, error) {
|
||||
op := &request.Operation{
|
||||
Name: "GetMetadata",
|
||||
HTTPMethod: "GET",
|
||||
|
@ -59,6 +68,8 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) {
|
|||
|
||||
req := c.NewRequest(op, nil, output)
|
||||
|
||||
req.SetContext(ctx)
|
||||
|
||||
err := req.Send()
|
||||
return output.Content, err
|
||||
}
|
||||
|
@ -67,6 +78,13 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) {
|
|||
// there is no user-data setup for the EC2 instance a "NotFoundError" error
|
||||
// code will be returned.
|
||||
func (c *EC2Metadata) GetUserData() (string, error) {
|
||||
return c.GetUserDataWithContext(aws.BackgroundContext())
|
||||
}
|
||||
|
||||
// GetUserDataWithContext returns the userdata that was configured for the service. If
|
||||
// there is no user-data setup for the EC2 instance a "NotFoundError" error
|
||||
// code will be returned.
|
||||
func (c *EC2Metadata) GetUserDataWithContext(ctx aws.Context) (string, error) {
|
||||
op := &request.Operation{
|
||||
Name: "GetUserData",
|
||||
HTTPMethod: "GET",
|
||||
|
@ -75,6 +93,7 @@ func (c *EC2Metadata) GetUserData() (string, error) {
|
|||
|
||||
output := &metadataOutput{}
|
||||
req := c.NewRequest(op, nil, output)
|
||||
req.SetContext(ctx)
|
||||
|
||||
err := req.Send()
|
||||
return output.Content, err
|
||||
|
@ -84,6 +103,13 @@ func (c *EC2Metadata) GetUserData() (string, error) {
|
|||
// instance metadata service for dynamic data. The content will be returned
|
||||
// as a string, or error if the request failed.
|
||||
func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
|
||||
return c.GetDynamicDataWithContext(aws.BackgroundContext(), p)
|
||||
}
|
||||
|
||||
// GetDynamicDataWithContext uses the path provided to request information from the EC2
|
||||
// instance metadata service for dynamic data. The content will be returned
|
||||
// as a string, or error if the request failed.
|
||||
func (c *EC2Metadata) GetDynamicDataWithContext(ctx aws.Context, p string) (string, error) {
|
||||
op := &request.Operation{
|
||||
Name: "GetDynamicData",
|
||||
HTTPMethod: "GET",
|
||||
|
@ -92,6 +118,7 @@ func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
|
|||
|
||||
output := &metadataOutput{}
|
||||
req := c.NewRequest(op, nil, output)
|
||||
req.SetContext(ctx)
|
||||
|
||||
err := req.Send()
|
||||
return output.Content, err
|
||||
|
@ -101,7 +128,14 @@ func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
|
|||
// instance. Error is returned if the request fails or is unable to parse
|
||||
// the response.
|
||||
func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument, error) {
|
||||
resp, err := c.GetDynamicData("instance-identity/document")
|
||||
return c.GetInstanceIdentityDocumentWithContext(aws.BackgroundContext())
|
||||
}
|
||||
|
||||
// GetInstanceIdentityDocumentWithContext retrieves an identity document describing an
|
||||
// instance. Error is returned if the request fails or is unable to parse
|
||||
// the response.
|
||||
func (c *EC2Metadata) GetInstanceIdentityDocumentWithContext(ctx aws.Context) (EC2InstanceIdentityDocument, error) {
|
||||
resp, err := c.GetDynamicDataWithContext(ctx, "instance-identity/document")
|
||||
if err != nil {
|
||||
return EC2InstanceIdentityDocument{},
|
||||
awserr.New("EC2MetadataRequestError",
|
||||
|
@ -120,7 +154,12 @@ func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument
|
|||
|
||||
// IAMInfo retrieves IAM info from the metadata API
|
||||
func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) {
|
||||
resp, err := c.GetMetadata("iam/info")
|
||||
return c.IAMInfoWithContext(aws.BackgroundContext())
|
||||
}
|
||||
|
||||
// IAMInfoWithContext retrieves IAM info from the metadata API
|
||||
func (c *EC2Metadata) IAMInfoWithContext(ctx aws.Context) (EC2IAMInfo, error) {
|
||||
resp, err := c.GetMetadataWithContext(ctx, "iam/info")
|
||||
if err != nil {
|
||||
return EC2IAMInfo{},
|
||||
awserr.New("EC2MetadataRequestError",
|
||||
|
@ -145,7 +184,12 @@ func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) {
|
|||
|
||||
// Region returns the region the instance is running in.
|
||||
func (c *EC2Metadata) Region() (string, error) {
|
||||
ec2InstanceIdentityDocument, err := c.GetInstanceIdentityDocument()
|
||||
return c.RegionWithContext(aws.BackgroundContext())
|
||||
}
|
||||
|
||||
// RegionWithContext returns the region the instance is running in.
|
||||
func (c *EC2Metadata) RegionWithContext(ctx aws.Context) (string, error) {
|
||||
ec2InstanceIdentityDocument, err := c.GetInstanceIdentityDocumentWithContext(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -162,7 +206,14 @@ func (c *EC2Metadata) Region() (string, error) {
|
|||
// Can be used to determine if application is running within an EC2 Instance and
|
||||
// the metadata service is available.
|
||||
func (c *EC2Metadata) Available() bool {
|
||||
if _, err := c.GetMetadata("instance-id"); err != nil {
|
||||
return c.AvailableWithContext(aws.BackgroundContext())
|
||||
}
|
||||
|
||||
// AvailableWithContext returns if the application has access to the EC2 Metadata service.
|
||||
// Can be used to determine if application is running within an EC2 Instance and
|
||||
// the metadata service is available.
|
||||
func (c *EC2Metadata) AvailableWithContext(ctx aws.Context) bool {
|
||||
if _, err := c.GetMetadataWithContext(ctx, "instance-id"); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ func (t *tokenProvider) fetchTokenHandler(r *request.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
output, err := t.client.getToken(t.configuredTTL)
|
||||
output, err := t.client.getToken(r.Context(), t.configuredTTL)
|
||||
|
||||
if err != nil {
|
||||
|
||||
|
|
|
@ -1139,11 +1139,41 @@ var awsPartition = partition{
|
|||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"fips-ca-central-1": endpoint{
|
||||
Hostname: "codepipeline-fips.ca-central-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ca-central-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "codepipeline-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "codepipeline-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "codepipeline-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "codepipeline-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"codestar": service{
|
||||
|
@ -1174,6 +1204,7 @@ var awsPartition = partition{
|
|||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-north-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
|
@ -1755,12 +1786,59 @@ var awsPartition = partition{
|
|||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "ecs-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "ecs-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "ecs-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"madison-fips-us-west-2": endpoint{
|
||||
Hostname: "ecs-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"elastic-inference": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{
|
||||
Hostname: "api.elastic-inference.ap-northeast-1.amazonaws.com",
|
||||
},
|
||||
"ap-northeast-2": endpoint{
|
||||
Hostname: "api.elastic-inference.ap-northeast-2.amazonaws.com",
|
||||
},
|
||||
"eu-west-1": endpoint{
|
||||
Hostname: "api.elastic-inference.eu-west-1.amazonaws.com",
|
||||
},
|
||||
"us-east-1": endpoint{
|
||||
Hostname: "api.elastic-inference.us-east-1.amazonaws.com",
|
||||
},
|
||||
"us-east-2": endpoint{
|
||||
Hostname: "api.elastic-inference.us-east-2.amazonaws.com",
|
||||
},
|
||||
"us-west-2": endpoint{
|
||||
Hostname: "api.elastic-inference.us-west-2.amazonaws.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
"elasticache": service{
|
||||
|
@ -1807,12 +1885,36 @@ var awsPartition = partition{
|
|||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "elasticbeanstalk-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "elasticbeanstalk-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "elasticbeanstalk-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "elasticbeanstalk-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"elasticfilesystem": service{
|
||||
|
@ -2072,12 +2174,36 @@ var awsPartition = partition{
|
|||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "firehose-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "firehose-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "firehose-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "firehose-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"fms": service{
|
||||
|
@ -2226,6 +2352,7 @@ var awsPartition = partition{
|
|||
"fsx": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-east-1": endpoint{},
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
|
@ -2358,10 +2485,11 @@ var awsPartition = partition{
|
|||
"groundstation": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"eu-north-1": endpoint{},
|
||||
"me-south-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-north-1": endpoint{},
|
||||
"me-south-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"guardduty": service{
|
||||
|
@ -2689,12 +2817,36 @@ var awsPartition = partition{
|
|||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "kinesis-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "kinesis-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "kinesis-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "kinesis-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"kinesisanalytics": service{
|
||||
|
@ -2818,12 +2970,36 @@ var awsPartition = partition{
|
|||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "license-manager-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "license-manager-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "license-manager-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "license-manager-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"lightsail": service{
|
||||
|
@ -3052,12 +3228,36 @@ var awsPartition = partition{
|
|||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "monitoring-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "monitoring-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "monitoring-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "monitoring-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"mq": service{
|
||||
|
@ -4599,25 +4799,25 @@ var awsPartition = partition{
|
|||
"me-south-1": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"ssm-facade-fips-us-east-1": endpoint{
|
||||
Hostname: "#ssm-facade-fips.us-east-1.amazonaws.com",
|
||||
Hostname: "ssm-facade-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"ssm-facade-fips-us-east-2": endpoint{
|
||||
Hostname: "#ssm-facade-fips.us-east-2.amazonaws.com",
|
||||
Hostname: "ssm-facade-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"ssm-facade-fips-us-west-1": endpoint{
|
||||
Hostname: "#ssm-facade-fips.us-west-1.amazonaws.com",
|
||||
Hostname: "ssm-facade-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"ssm-facade-fips-us-west-2": endpoint{
|
||||
Hostname: "#ssm-facade-fips.us-west-2.amazonaws.com",
|
||||
Hostname: "ssm-facade-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
|
@ -5024,6 +5224,12 @@ var awsPartition = partition{
|
|||
IsRegionalized: boxedFalse,
|
||||
|
||||
Endpoints: endpoints{
|
||||
"aws-fips": endpoint{
|
||||
Hostname: "waf-fips.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"aws-global": endpoint{
|
||||
Hostname: "waf.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
|
@ -5035,22 +5241,222 @@ var awsPartition = partition{
|
|||
"waf-regional": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"ap-northeast-1": endpoint{},
|
||||
"ap-northeast-2": endpoint{},
|
||||
"ap-south-1": endpoint{},
|
||||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"ca-central-1": endpoint{},
|
||||
"eu-central-1": endpoint{},
|
||||
"eu-north-1": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"eu-west-2": endpoint{},
|
||||
"eu-west-3": endpoint{},
|
||||
"sa-east-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-east-2": endpoint{},
|
||||
"us-west-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"ap-east-1": endpoint{
|
||||
Hostname: "waf-regional.ap-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-east-1",
|
||||
},
|
||||
},
|
||||
"ap-northeast-1": endpoint{
|
||||
Hostname: "waf-regional.ap-northeast-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-northeast-1",
|
||||
},
|
||||
},
|
||||
"ap-northeast-2": endpoint{
|
||||
Hostname: "waf-regional.ap-northeast-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-northeast-2",
|
||||
},
|
||||
},
|
||||
"ap-south-1": endpoint{
|
||||
Hostname: "waf-regional.ap-south-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-south-1",
|
||||
},
|
||||
},
|
||||
"ap-southeast-1": endpoint{
|
||||
Hostname: "waf-regional.ap-southeast-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-southeast-1",
|
||||
},
|
||||
},
|
||||
"ap-southeast-2": endpoint{
|
||||
Hostname: "waf-regional.ap-southeast-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-southeast-2",
|
||||
},
|
||||
},
|
||||
"ca-central-1": endpoint{
|
||||
Hostname: "waf-regional.ca-central-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ca-central-1",
|
||||
},
|
||||
},
|
||||
"eu-central-1": endpoint{
|
||||
Hostname: "waf-regional.eu-central-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-central-1",
|
||||
},
|
||||
},
|
||||
"eu-north-1": endpoint{
|
||||
Hostname: "waf-regional.eu-north-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-north-1",
|
||||
},
|
||||
},
|
||||
"eu-west-1": endpoint{
|
||||
Hostname: "waf-regional.eu-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-west-1",
|
||||
},
|
||||
},
|
||||
"eu-west-2": endpoint{
|
||||
Hostname: "waf-regional.eu-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-west-2",
|
||||
},
|
||||
},
|
||||
"eu-west-3": endpoint{
|
||||
Hostname: "waf-regional.eu-west-3.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-west-3",
|
||||
},
|
||||
},
|
||||
"fips-ap-east-1": endpoint{
|
||||
Hostname: "waf-regional-fips.ap-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-east-1",
|
||||
},
|
||||
},
|
||||
"fips-ap-northeast-1": endpoint{
|
||||
Hostname: "waf-regional-fips.ap-northeast-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-northeast-1",
|
||||
},
|
||||
},
|
||||
"fips-ap-northeast-2": endpoint{
|
||||
Hostname: "waf-regional-fips.ap-northeast-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-northeast-2",
|
||||
},
|
||||
},
|
||||
"fips-ap-south-1": endpoint{
|
||||
Hostname: "waf-regional-fips.ap-south-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-south-1",
|
||||
},
|
||||
},
|
||||
"fips-ap-southeast-1": endpoint{
|
||||
Hostname: "waf-regional-fips.ap-southeast-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-southeast-1",
|
||||
},
|
||||
},
|
||||
"fips-ap-southeast-2": endpoint{
|
||||
Hostname: "waf-regional-fips.ap-southeast-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ap-southeast-2",
|
||||
},
|
||||
},
|
||||
"fips-ca-central-1": endpoint{
|
||||
Hostname: "waf-regional-fips.ca-central-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "ca-central-1",
|
||||
},
|
||||
},
|
||||
"fips-eu-central-1": endpoint{
|
||||
Hostname: "waf-regional-fips.eu-central-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-central-1",
|
||||
},
|
||||
},
|
||||
"fips-eu-north-1": endpoint{
|
||||
Hostname: "waf-regional-fips.eu-north-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-north-1",
|
||||
},
|
||||
},
|
||||
"fips-eu-west-1": endpoint{
|
||||
Hostname: "waf-regional-fips.eu-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-west-1",
|
||||
},
|
||||
},
|
||||
"fips-eu-west-2": endpoint{
|
||||
Hostname: "waf-regional-fips.eu-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-west-2",
|
||||
},
|
||||
},
|
||||
"fips-eu-west-3": endpoint{
|
||||
Hostname: "waf-regional-fips.eu-west-3.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "eu-west-3",
|
||||
},
|
||||
},
|
||||
"fips-me-south-1": endpoint{
|
||||
Hostname: "waf-regional-fips.me-south-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "me-south-1",
|
||||
},
|
||||
},
|
||||
"fips-sa-east-1": endpoint{
|
||||
Hostname: "waf-regional-fips.sa-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "sa-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "waf-regional-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-east-2": endpoint{
|
||||
Hostname: "waf-regional-fips.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"fips-us-west-1": endpoint{
|
||||
Hostname: "waf-regional-fips.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "waf-regional-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"me-south-1": endpoint{
|
||||
Hostname: "waf-regional.me-south-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "me-south-1",
|
||||
},
|
||||
},
|
||||
"sa-east-1": endpoint{
|
||||
Hostname: "waf-regional.sa-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "sa-east-1",
|
||||
},
|
||||
},
|
||||
"us-east-1": endpoint{
|
||||
Hostname: "waf-regional.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"us-east-2": endpoint{
|
||||
Hostname: "waf-regional.us-east-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-2",
|
||||
},
|
||||
},
|
||||
"us-west-1": endpoint{
|
||||
Hostname: "waf-regional.us-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-1",
|
||||
},
|
||||
},
|
||||
"us-west-2": endpoint{
|
||||
Hostname: "waf-regional.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"workdocs": service{
|
||||
|
@ -5060,8 +5466,20 @@ var awsPartition = partition{
|
|||
"ap-southeast-1": endpoint{},
|
||||
"ap-southeast-2": endpoint{},
|
||||
"eu-west-1": endpoint{},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
"fips-us-east-1": endpoint{
|
||||
Hostname: "workdocs-fips.us-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-west-2": endpoint{
|
||||
Hostname: "workdocs-fips.us-west-2.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-west-2",
|
||||
},
|
||||
},
|
||||
"us-east-1": endpoint{},
|
||||
"us-west-2": endpoint{},
|
||||
},
|
||||
},
|
||||
"workmail": service{
|
||||
|
@ -5962,7 +6380,19 @@ var awsusgovPartition = partition{
|
|||
|
||||
Endpoints: endpoints{
|
||||
"us-gov-east-1": endpoint{},
|
||||
"us-gov-east-1-fips": endpoint{
|
||||
Hostname: "codebuild-fips.us-gov-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-east-1",
|
||||
},
|
||||
},
|
||||
"us-gov-west-1": endpoint{},
|
||||
"us-gov-west-1-fips": endpoint{
|
||||
Hostname: "codebuild-fips.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"codecommit": service{
|
||||
|
@ -5997,6 +6427,18 @@ var awsusgovPartition = partition{
|
|||
},
|
||||
},
|
||||
},
|
||||
"codepipeline": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"fips-us-gov-west-1": endpoint{
|
||||
Hostname: "codepipeline-fips.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
},
|
||||
"comprehend": service{
|
||||
Defaults: endpoint{
|
||||
Protocols: []string{"https"},
|
||||
|
@ -6110,6 +6552,18 @@ var awsusgovPartition = partition{
|
|||
"ecs": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"fips-us-gov-east-1": endpoint{
|
||||
Hostname: "ecs-fips.us-gov-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-gov-west-1": endpoint{
|
||||
Hostname: "ecs-fips.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
"us-gov-east-1": endpoint{},
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
|
@ -6130,8 +6584,18 @@ var awsusgovPartition = partition{
|
|||
"elasticbeanstalk": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"us-gov-east-1": endpoint{},
|
||||
"us-gov-west-1": endpoint{},
|
||||
"us-gov-east-1": endpoint{
|
||||
Hostname: "elasticbeanstalk.us-gov-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-east-1",
|
||||
},
|
||||
},
|
||||
"us-gov-west-1": endpoint{
|
||||
Hostname: "elasticbeanstalk.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"elasticfilesystem": service{
|
||||
|
@ -6182,6 +6646,18 @@ var awsusgovPartition = partition{
|
|||
"firehose": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"fips-us-gov-east-1": endpoint{
|
||||
Hostname: "firehose-fips.us-gov-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-gov-west-1": endpoint{
|
||||
Hostname: "firehose-fips.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
"us-gov-east-1": endpoint{},
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
|
@ -6313,6 +6789,18 @@ var awsusgovPartition = partition{
|
|||
"license-manager": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"fips-us-gov-east-1": endpoint{
|
||||
Hostname: "license-manager-fips.us-gov-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-gov-west-1": endpoint{
|
||||
Hostname: "license-manager-fips.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
"us-gov-east-1": endpoint{},
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
|
@ -6344,6 +6832,18 @@ var awsusgovPartition = partition{
|
|||
"monitoring": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"fips-us-gov-east-1": endpoint{
|
||||
Hostname: "monitoring.us-gov-east-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-east-1",
|
||||
},
|
||||
},
|
||||
"fips-us-gov-west-1": endpoint{
|
||||
Hostname: "monitoring.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
"us-gov-east-1": endpoint{},
|
||||
"us-gov-west-1": endpoint{},
|
||||
},
|
||||
|
@ -6781,7 +7281,18 @@ var awsusgovPartition = partition{
|
|||
"waf-regional": service{
|
||||
|
||||
Endpoints: endpoints{
|
||||
"us-gov-west-1": endpoint{},
|
||||
"fips-us-gov-west-1": endpoint{
|
||||
Hostname: "waf-regional-fips.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
"us-gov-west-1": endpoint{
|
||||
Hostname: "waf-regional.us-gov-west-1.amazonaws.com",
|
||||
CredentialScope: credentialScope{
|
||||
Region: "us-gov-west-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"workspaces": service{
|
||||
|
|
|
@ -5,4 +5,4 @@ package aws
|
|||
const SDKName = "aws-sdk-go"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = "1.29.34"
|
||||
const SDKVersion = "1.30.5"
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- tip
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2013 Kelsey Hightower
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
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.
|
|
@ -0,0 +1,2 @@
|
|||
Kelsey Hightower kelsey.hightower@gmail.com github.com/kelseyhightower
|
||||
Travis Parker travis.parker@gmail.com github.com/teepark
|
|
@ -0,0 +1,192 @@
|
|||
# envconfig
|
||||
|
||||
[](https://travis-ci.org/kelseyhightower/envconfig)
|
||||
|
||||
```Go
|
||||
import "github.com/kelseyhightower/envconfig"
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
See [godoc](http://godoc.org/github.com/kelseyhightower/envconfig)
|
||||
|
||||
## Usage
|
||||
|
||||
Set some environment variables:
|
||||
|
||||
```Bash
|
||||
export MYAPP_DEBUG=false
|
||||
export MYAPP_PORT=8080
|
||||
export MYAPP_USER=Kelsey
|
||||
export MYAPP_RATE="0.5"
|
||||
export MYAPP_TIMEOUT="3m"
|
||||
export MYAPP_USERS="rob,ken,robert"
|
||||
export MYAPP_COLORCODES="red:1,green:2,blue:3"
|
||||
```
|
||||
|
||||
Write some code:
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
)
|
||||
|
||||
type Specification struct {
|
||||
Debug bool
|
||||
Port int
|
||||
User string
|
||||
Users []string
|
||||
Rate float32
|
||||
Timeout time.Duration
|
||||
ColorCodes map[string]int
|
||||
}
|
||||
|
||||
func main() {
|
||||
var s Specification
|
||||
err := envconfig.Process("myapp", &s)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
format := "Debug: %v\nPort: %d\nUser: %s\nRate: %f\nTimeout: %s\n"
|
||||
_, err = fmt.Printf(format, s.Debug, s.Port, s.User, s.Rate, s.Timeout)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
|
||||
fmt.Println("Users:")
|
||||
for _, u := range s.Users {
|
||||
fmt.Printf(" %s\n", u)
|
||||
}
|
||||
|
||||
fmt.Println("Color codes:")
|
||||
for k, v := range s.ColorCodes {
|
||||
fmt.Printf(" %s: %d\n", k, v)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Results:
|
||||
|
||||
```Bash
|
||||
Debug: false
|
||||
Port: 8080
|
||||
User: Kelsey
|
||||
Rate: 0.500000
|
||||
Timeout: 3m0s
|
||||
Users:
|
||||
rob
|
||||
ken
|
||||
robert
|
||||
Color codes:
|
||||
red: 1
|
||||
green: 2
|
||||
blue: 3
|
||||
```
|
||||
|
||||
## Struct Tag Support
|
||||
|
||||
Envconfig supports the use of struct tags to specify alternate, default, and required
|
||||
environment variables.
|
||||
|
||||
For example, consider the following struct:
|
||||
|
||||
```Go
|
||||
type Specification struct {
|
||||
ManualOverride1 string `envconfig:"manual_override_1"`
|
||||
DefaultVar string `default:"foobar"`
|
||||
RequiredVar string `required:"true"`
|
||||
IgnoredVar string `ignored:"true"`
|
||||
AutoSplitVar string `split_words:"true"`
|
||||
RequiredAndAutoSplitVar string `required:"true" split_words:"true"`
|
||||
}
|
||||
```
|
||||
|
||||
Envconfig has automatic support for CamelCased struct elements when the
|
||||
`split_words:"true"` tag is supplied. Without this tag, `AutoSplitVar` above
|
||||
would look for an environment variable called `MYAPP_AUTOSPLITVAR`. With the
|
||||
setting applied it will look for `MYAPP_AUTO_SPLIT_VAR`. Note that numbers
|
||||
will get globbed into the previous word. If the setting does not do the
|
||||
right thing, you may use a manual override.
|
||||
|
||||
Envconfig will process value for `ManualOverride1` by populating it with the
|
||||
value for `MYAPP_MANUAL_OVERRIDE_1`. Without this struct tag, it would have
|
||||
instead looked up `MYAPP_MANUALOVERRIDE1`. With the `split_words:"true"` tag
|
||||
it would have looked up `MYAPP_MANUAL_OVERRIDE1`.
|
||||
|
||||
```Bash
|
||||
export MYAPP_MANUAL_OVERRIDE_1="this will be the value"
|
||||
|
||||
# export MYAPP_MANUALOVERRIDE1="and this will not"
|
||||
```
|
||||
|
||||
If envconfig can't find an environment variable value for `MYAPP_DEFAULTVAR`,
|
||||
it will populate it with "foobar" as a default value.
|
||||
|
||||
If envconfig can't find an environment variable value for `MYAPP_REQUIREDVAR`,
|
||||
it will return an error when asked to process the struct. If
|
||||
`MYAPP_REQUIREDVAR` is present but empty, envconfig will not return an error.
|
||||
|
||||
If envconfig can't find an environment variable in the form `PREFIX_MYVAR`, and there
|
||||
is a struct tag defined, it will try to populate your variable with an environment
|
||||
variable that directly matches the envconfig tag in your struct definition:
|
||||
|
||||
```shell
|
||||
export SERVICE_HOST=127.0.0.1
|
||||
export MYAPP_DEBUG=true
|
||||
```
|
||||
```Go
|
||||
type Specification struct {
|
||||
ServiceHost string `envconfig:"SERVICE_HOST"`
|
||||
Debug bool
|
||||
}
|
||||
```
|
||||
|
||||
Envconfig won't process a field with the "ignored" tag set to "true", even if a corresponding
|
||||
environment variable is set.
|
||||
|
||||
## Supported Struct Field Types
|
||||
|
||||
envconfig supports these struct field types:
|
||||
|
||||
* string
|
||||
* int8, int16, int32, int64
|
||||
* bool
|
||||
* float32, float64
|
||||
* slices of any supported type
|
||||
* maps (keys and values of any supported type)
|
||||
* [encoding.TextUnmarshaler](https://golang.org/pkg/encoding/#TextUnmarshaler)
|
||||
* [encoding.BinaryUnmarshaler](https://golang.org/pkg/encoding/#BinaryUnmarshaler)
|
||||
* [time.Duration](https://golang.org/pkg/time/#Duration)
|
||||
|
||||
Embedded structs using these fields are also supported.
|
||||
|
||||
## Custom Decoders
|
||||
|
||||
Any field whose type (or pointer-to-type) implements `envconfig.Decoder` can
|
||||
control its own deserialization:
|
||||
|
||||
```Bash
|
||||
export DNS_SERVER=8.8.8.8
|
||||
```
|
||||
|
||||
```Go
|
||||
type IPDecoder net.IP
|
||||
|
||||
func (ipd *IPDecoder) Decode(value string) error {
|
||||
*ipd = IPDecoder(net.ParseIP(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
type DNSConfig struct {
|
||||
Address IPDecoder `envconfig:"DNS_SERVER"`
|
||||
}
|
||||
```
|
||||
|
||||
Also, envconfig will use a `Set(string) error` method like from the
|
||||
[flag.Value](https://godoc.org/flag#Value) interface if implemented.
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
|
||||
// Use of this source code is governed by the MIT License that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
// Package envconfig implements decoding of environment variables based on a user
|
||||
// defined specification. A typical use is using environment variables for
|
||||
// configuration settings.
|
||||
package envconfig
|
|
@ -0,0 +1,7 @@
|
|||
// +build appengine go1.5
|
||||
|
||||
package envconfig
|
||||
|
||||
import "os"
|
||||
|
||||
var lookupEnv = os.LookupEnv
|
|
@ -0,0 +1,7 @@
|
|||
// +build !appengine,!go1.5
|
||||
|
||||
package envconfig
|
||||
|
||||
import "syscall"
|
||||
|
||||
var lookupEnv = syscall.Getenv
|
|
@ -0,0 +1,382 @@
|
|||
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
|
||||
// Use of this source code is governed by the MIT License that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrInvalidSpecification indicates that a specification is of the wrong type.
|
||||
var ErrInvalidSpecification = errors.New("specification must be a struct pointer")
|
||||
|
||||
var gatherRegexp = regexp.MustCompile("([^A-Z]+|[A-Z]+[^A-Z]+|[A-Z]+)")
|
||||
var acronymRegexp = regexp.MustCompile("([A-Z]+)([A-Z][^A-Z]+)")
|
||||
|
||||
// A ParseError occurs when an environment variable cannot be converted to
|
||||
// the type required by a struct field during assignment.
|
||||
type ParseError struct {
|
||||
KeyName string
|
||||
FieldName string
|
||||
TypeName string
|
||||
Value string
|
||||
Err error
|
||||
}
|
||||
|
||||
// Decoder has the same semantics as Setter, but takes higher precedence.
|
||||
// It is provided for historical compatibility.
|
||||
type Decoder interface {
|
||||
Decode(value string) error
|
||||
}
|
||||
|
||||
// Setter is implemented by types can self-deserialize values.
|
||||
// Any type that implements flag.Value also implements Setter.
|
||||
type Setter interface {
|
||||
Set(value string) error
|
||||
}
|
||||
|
||||
func (e *ParseError) Error() string {
|
||||
return fmt.Sprintf("envconfig.Process: assigning %[1]s to %[2]s: converting '%[3]s' to type %[4]s. details: %[5]s", e.KeyName, e.FieldName, e.Value, e.TypeName, e.Err)
|
||||
}
|
||||
|
||||
// varInfo maintains information about the configuration variable
|
||||
type varInfo struct {
|
||||
Name string
|
||||
Alt string
|
||||
Key string
|
||||
Field reflect.Value
|
||||
Tags reflect.StructTag
|
||||
}
|
||||
|
||||
// GatherInfo gathers information about the specified struct
|
||||
func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) {
|
||||
s := reflect.ValueOf(spec)
|
||||
|
||||
if s.Kind() != reflect.Ptr {
|
||||
return nil, ErrInvalidSpecification
|
||||
}
|
||||
s = s.Elem()
|
||||
if s.Kind() != reflect.Struct {
|
||||
return nil, ErrInvalidSpecification
|
||||
}
|
||||
typeOfSpec := s.Type()
|
||||
|
||||
// over allocate an info array, we will extend if needed later
|
||||
infos := make([]varInfo, 0, s.NumField())
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
ftype := typeOfSpec.Field(i)
|
||||
if !f.CanSet() || isTrue(ftype.Tag.Get("ignored")) {
|
||||
continue
|
||||
}
|
||||
|
||||
for f.Kind() == reflect.Ptr {
|
||||
if f.IsNil() {
|
||||
if f.Type().Elem().Kind() != reflect.Struct {
|
||||
// nil pointer to a non-struct: leave it alone
|
||||
break
|
||||
}
|
||||
// nil pointer to struct: create a zero instance
|
||||
f.Set(reflect.New(f.Type().Elem()))
|
||||
}
|
||||
f = f.Elem()
|
||||
}
|
||||
|
||||
// Capture information about the config variable
|
||||
info := varInfo{
|
||||
Name: ftype.Name,
|
||||
Field: f,
|
||||
Tags: ftype.Tag,
|
||||
Alt: strings.ToUpper(ftype.Tag.Get("envconfig")),
|
||||
}
|
||||
|
||||
// Default to the field name as the env var name (will be upcased)
|
||||
info.Key = info.Name
|
||||
|
||||
// Best effort to un-pick camel casing as separate words
|
||||
if isTrue(ftype.Tag.Get("split_words")) {
|
||||
words := gatherRegexp.FindAllStringSubmatch(ftype.Name, -1)
|
||||
if len(words) > 0 {
|
||||
var name []string
|
||||
for _, words := range words {
|
||||
if m := acronymRegexp.FindStringSubmatch(words[0]); len(m) == 3 {
|
||||
name = append(name, m[1], m[2])
|
||||
} else {
|
||||
name = append(name, words[0])
|
||||
}
|
||||
}
|
||||
|
||||
info.Key = strings.Join(name, "_")
|
||||
}
|
||||
}
|
||||
if info.Alt != "" {
|
||||
info.Key = info.Alt
|
||||
}
|
||||
if prefix != "" {
|
||||
info.Key = fmt.Sprintf("%s_%s", prefix, info.Key)
|
||||
}
|
||||
info.Key = strings.ToUpper(info.Key)
|
||||
infos = append(infos, info)
|
||||
|
||||
if f.Kind() == reflect.Struct {
|
||||
// honor Decode if present
|
||||
if decoderFrom(f) == nil && setterFrom(f) == nil && textUnmarshaler(f) == nil && binaryUnmarshaler(f) == nil {
|
||||
innerPrefix := prefix
|
||||
if !ftype.Anonymous {
|
||||
innerPrefix = info.Key
|
||||
}
|
||||
|
||||
embeddedPtr := f.Addr().Interface()
|
||||
embeddedInfos, err := gatherInfo(innerPrefix, embeddedPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infos = append(infos[:len(infos)-1], embeddedInfos...)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
// CheckDisallowed checks that no environment variables with the prefix are set
|
||||
// that we don't know how or want to parse. This is likely only meaningful with
|
||||
// a non-empty prefix.
|
||||
func CheckDisallowed(prefix string, spec interface{}) error {
|
||||
infos, err := gatherInfo(prefix, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vars := make(map[string]struct{})
|
||||
for _, info := range infos {
|
||||
vars[info.Key] = struct{}{}
|
||||
}
|
||||
|
||||
if prefix != "" {
|
||||
prefix = strings.ToUpper(prefix) + "_"
|
||||
}
|
||||
|
||||
for _, env := range os.Environ() {
|
||||
if !strings.HasPrefix(env, prefix) {
|
||||
continue
|
||||
}
|
||||
v := strings.SplitN(env, "=", 2)[0]
|
||||
if _, found := vars[v]; !found {
|
||||
return fmt.Errorf("unknown environment variable %s", v)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process populates the specified struct based on environment variables
|
||||
func Process(prefix string, spec interface{}) error {
|
||||
infos, err := gatherInfo(prefix, spec)
|
||||
|
||||
for _, info := range infos {
|
||||
|
||||
// `os.Getenv` cannot differentiate between an explicitly set empty value
|
||||
// and an unset value. `os.LookupEnv` is preferred to `syscall.Getenv`,
|
||||
// but it is only available in go1.5 or newer. We're using Go build tags
|
||||
// here to use os.LookupEnv for >=go1.5
|
||||
value, ok := lookupEnv(info.Key)
|
||||
if !ok && info.Alt != "" {
|
||||
value, ok = lookupEnv(info.Alt)
|
||||
}
|
||||
|
||||
def := info.Tags.Get("default")
|
||||
if def != "" && !ok {
|
||||
value = def
|
||||
}
|
||||
|
||||
req := info.Tags.Get("required")
|
||||
if !ok && def == "" {
|
||||
if isTrue(req) {
|
||||
key := info.Key
|
||||
if info.Alt != "" {
|
||||
key = info.Alt
|
||||
}
|
||||
return fmt.Errorf("required key %s missing value", key)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = processField(value, info.Field)
|
||||
if err != nil {
|
||||
return &ParseError{
|
||||
KeyName: info.Key,
|
||||
FieldName: info.Name,
|
||||
TypeName: info.Field.Type().String(),
|
||||
Value: value,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// MustProcess is the same as Process but panics if an error occurs
|
||||
func MustProcess(prefix string, spec interface{}) {
|
||||
if err := Process(prefix, spec); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func processField(value string, field reflect.Value) error {
|
||||
typ := field.Type()
|
||||
|
||||
decoder := decoderFrom(field)
|
||||
if decoder != nil {
|
||||
return decoder.Decode(value)
|
||||
}
|
||||
// look for Set method if Decode not defined
|
||||
setter := setterFrom(field)
|
||||
if setter != nil {
|
||||
return setter.Set(value)
|
||||
}
|
||||
|
||||
if t := textUnmarshaler(field); t != nil {
|
||||
return t.UnmarshalText([]byte(value))
|
||||
}
|
||||
|
||||
if b := binaryUnmarshaler(field); b != nil {
|
||||
return b.UnmarshalBinary([]byte(value))
|
||||
}
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
if field.IsNil() {
|
||||
field.Set(reflect.New(typ))
|
||||
}
|
||||
field = field.Elem()
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.String:
|
||||
field.SetString(value)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
var (
|
||||
val int64
|
||||
err error
|
||||
)
|
||||
if field.Kind() == reflect.Int64 && typ.PkgPath() == "time" && typ.Name() == "Duration" {
|
||||
var d time.Duration
|
||||
d, err = time.ParseDuration(value)
|
||||
val = int64(d)
|
||||
} else {
|
||||
val, err = strconv.ParseInt(value, 0, typ.Bits())
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
field.SetInt(val)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
val, err := strconv.ParseUint(value, 0, typ.Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetUint(val)
|
||||
case reflect.Bool:
|
||||
val, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetBool(val)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
val, err := strconv.ParseFloat(value, typ.Bits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetFloat(val)
|
||||
case reflect.Slice:
|
||||
sl := reflect.MakeSlice(typ, 0, 0)
|
||||
if typ.Elem().Kind() == reflect.Uint8 {
|
||||
sl = reflect.ValueOf([]byte(value))
|
||||
} else if len(strings.TrimSpace(value)) != 0 {
|
||||
vals := strings.Split(value, ",")
|
||||
sl = reflect.MakeSlice(typ, len(vals), len(vals))
|
||||
for i, val := range vals {
|
||||
err := processField(val, sl.Index(i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
field.Set(sl)
|
||||
case reflect.Map:
|
||||
mp := reflect.MakeMap(typ)
|
||||
if len(strings.TrimSpace(value)) != 0 {
|
||||
pairs := strings.Split(value, ",")
|
||||
for _, pair := range pairs {
|
||||
kvpair := strings.Split(pair, ":")
|
||||
if len(kvpair) != 2 {
|
||||
return fmt.Errorf("invalid map item: %q", pair)
|
||||
}
|
||||
k := reflect.New(typ.Key()).Elem()
|
||||
err := processField(kvpair[0], k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := reflect.New(typ.Elem()).Elem()
|
||||
err = processField(kvpair[1], v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mp.SetMapIndex(k, v)
|
||||
}
|
||||
}
|
||||
field.Set(mp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func interfaceFrom(field reflect.Value, fn func(interface{}, *bool)) {
|
||||
// it may be impossible for a struct field to fail this check
|
||||
if !field.CanInterface() {
|
||||
return
|
||||
}
|
||||
var ok bool
|
||||
fn(field.Interface(), &ok)
|
||||
if !ok && field.CanAddr() {
|
||||
fn(field.Addr().Interface(), &ok)
|
||||
}
|
||||
}
|
||||
|
||||
func decoderFrom(field reflect.Value) (d Decoder) {
|
||||
interfaceFrom(field, func(v interface{}, ok *bool) { d, *ok = v.(Decoder) })
|
||||
return d
|
||||
}
|
||||
|
||||
func setterFrom(field reflect.Value) (s Setter) {
|
||||
interfaceFrom(field, func(v interface{}, ok *bool) { s, *ok = v.(Setter) })
|
||||
return s
|
||||
}
|
||||
|
||||
func textUnmarshaler(field reflect.Value) (t encoding.TextUnmarshaler) {
|
||||
interfaceFrom(field, func(v interface{}, ok *bool) { t, *ok = v.(encoding.TextUnmarshaler) })
|
||||
return t
|
||||
}
|
||||
|
||||
func binaryUnmarshaler(field reflect.Value) (b encoding.BinaryUnmarshaler) {
|
||||
interfaceFrom(field, func(v interface{}, ok *bool) { b, *ok = v.(encoding.BinaryUnmarshaler) })
|
||||
return b
|
||||
}
|
||||
|
||||
func isTrue(s string) bool {
|
||||
b, _ := strconv.ParseBool(s)
|
||||
return b
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
module github.com/kelseyhightower/envconfig
|
|
@ -0,0 +1,164 @@
|
|||
// Copyright (c) 2016 Kelsey Hightower and others. All rights reserved.
|
||||
// Use of this source code is governed by the MIT License that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
package envconfig
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultListFormat constant to use to display usage in a list format
|
||||
DefaultListFormat = `This application is configured via the environment. The following environment
|
||||
variables can be used:
|
||||
{{range .}}
|
||||
{{usage_key .}}
|
||||
[description] {{usage_description .}}
|
||||
[type] {{usage_type .}}
|
||||
[default] {{usage_default .}}
|
||||
[required] {{usage_required .}}{{end}}
|
||||
`
|
||||
// DefaultTableFormat constant to use to display usage in a tabular format
|
||||
DefaultTableFormat = `This application is configured via the environment. The following environment
|
||||
variables can be used:
|
||||
|
||||
KEY TYPE DEFAULT REQUIRED DESCRIPTION
|
||||
{{range .}}{{usage_key .}} {{usage_type .}} {{usage_default .}} {{usage_required .}} {{usage_description .}}
|
||||
{{end}}`
|
||||
)
|
||||
|
||||
var (
|
||||
decoderType = reflect.TypeOf((*Decoder)(nil)).Elem()
|
||||
setterType = reflect.TypeOf((*Setter)(nil)).Elem()
|
||||
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
func implementsInterface(t reflect.Type) bool {
|
||||
return t.Implements(decoderType) ||
|
||||
reflect.PtrTo(t).Implements(decoderType) ||
|
||||
t.Implements(setterType) ||
|
||||
reflect.PtrTo(t).Implements(setterType) ||
|
||||
t.Implements(textUnmarshalerType) ||
|
||||
reflect.PtrTo(t).Implements(textUnmarshalerType) ||
|
||||
t.Implements(binaryUnmarshalerType) ||
|
||||
reflect.PtrTo(t).Implements(binaryUnmarshalerType)
|
||||
}
|
||||
|
||||
// toTypeDescription converts Go types into a human readable description
|
||||
func toTypeDescription(t reflect.Type) string {
|
||||
switch t.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if t.Elem().Kind() == reflect.Uint8 {
|
||||
return "String"
|
||||
}
|
||||
return fmt.Sprintf("Comma-separated list of %s", toTypeDescription(t.Elem()))
|
||||
case reflect.Map:
|
||||
return fmt.Sprintf(
|
||||
"Comma-separated list of %s:%s pairs",
|
||||
toTypeDescription(t.Key()),
|
||||
toTypeDescription(t.Elem()),
|
||||
)
|
||||
case reflect.Ptr:
|
||||
return toTypeDescription(t.Elem())
|
||||
case reflect.Struct:
|
||||
if implementsInterface(t) && t.Name() != "" {
|
||||
return t.Name()
|
||||
}
|
||||
return ""
|
||||
case reflect.String:
|
||||
name := t.Name()
|
||||
if name != "" && name != "string" {
|
||||
return name
|
||||
}
|
||||
return "String"
|
||||
case reflect.Bool:
|
||||
name := t.Name()
|
||||
if name != "" && name != "bool" {
|
||||
return name
|
||||
}
|
||||
return "True or False"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
name := t.Name()
|
||||
if name != "" && !strings.HasPrefix(name, "int") {
|
||||
return name
|
||||
}
|
||||
return "Integer"
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
name := t.Name()
|
||||
if name != "" && !strings.HasPrefix(name, "uint") {
|
||||
return name
|
||||
}
|
||||
return "Unsigned Integer"
|
||||
case reflect.Float32, reflect.Float64:
|
||||
name := t.Name()
|
||||
if name != "" && !strings.HasPrefix(name, "float") {
|
||||
return name
|
||||
}
|
||||
return "Float"
|
||||
}
|
||||
return fmt.Sprintf("%+v", t)
|
||||
}
|
||||
|
||||
// Usage writes usage information to stdout using the default header and table format
|
||||
func Usage(prefix string, spec interface{}) error {
|
||||
// The default is to output the usage information as a table
|
||||
// Create tabwriter instance to support table output
|
||||
tabs := tabwriter.NewWriter(os.Stdout, 1, 0, 4, ' ', 0)
|
||||
|
||||
err := Usagef(prefix, spec, tabs, DefaultTableFormat)
|
||||
tabs.Flush()
|
||||
return err
|
||||
}
|
||||
|
||||
// Usagef writes usage information to the specified io.Writer using the specifed template specification
|
||||
func Usagef(prefix string, spec interface{}, out io.Writer, format string) error {
|
||||
|
||||
// Specify the default usage template functions
|
||||
functions := template.FuncMap{
|
||||
"usage_key": func(v varInfo) string { return v.Key },
|
||||
"usage_description": func(v varInfo) string { return v.Tags.Get("desc") },
|
||||
"usage_type": func(v varInfo) string { return toTypeDescription(v.Field.Type()) },
|
||||
"usage_default": func(v varInfo) string { return v.Tags.Get("default") },
|
||||
"usage_required": func(v varInfo) (string, error) {
|
||||
req := v.Tags.Get("required")
|
||||
if req != "" {
|
||||
reqB, err := strconv.ParseBool(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if reqB {
|
||||
req = "true"
|
||||
}
|
||||
}
|
||||
return req, nil
|
||||
},
|
||||
}
|
||||
|
||||
tmpl, err := template.New("envconfig").Funcs(functions).Parse(format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Usaget(prefix, spec, out, tmpl)
|
||||
}
|
||||
|
||||
// Usaget writes usage information to the specified io.Writer using the specified template
|
||||
func Usaget(prefix string, spec interface{}, out io.Writer, tmpl *template.Template) error {
|
||||
// gather first
|
||||
infos, err := gatherInfo(prefix, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tmpl.Execute(out, infos)
|
||||
}
|
|
@ -8,7 +8,7 @@ ALL_PKGS := $(shell go list $(sort $(dir $(ALL_SRC))))
|
|||
GOTEST_OPT?=-v -race -timeout 30s
|
||||
GOTEST_OPT_WITH_COVERAGE = $(GOTEST_OPT) -coverprofile=coverage.txt -covermode=atomic
|
||||
GOTEST=go test
|
||||
GOFMT=gofmt
|
||||
GOIMPORTS=goimports
|
||||
GOLINT=golint
|
||||
GOVET=go vet
|
||||
EMBEDMD=embedmd
|
||||
|
@ -17,14 +17,14 @@ TRACE_ID_LINT_EXCEPTION="type name will be used as trace.TraceID by other packag
|
|||
TRACE_OPTION_LINT_EXCEPTION="type name will be used as trace.TraceOptions by other packages"
|
||||
README_FILES := $(shell find . -name '*README.md' | sort | tr '\n' ' ')
|
||||
|
||||
.DEFAULT_GOAL := fmt-lint-vet-embedmd-test
|
||||
.DEFAULT_GOAL := imports-lint-vet-embedmd-test
|
||||
|
||||
.PHONY: fmt-lint-vet-embedmd-test
|
||||
fmt-lint-vet-embedmd-test: fmt lint vet embedmd test
|
||||
.PHONY: imports-lint-vet-embedmd-test
|
||||
imports-lint-vet-embedmd-test: imports lint vet embedmd test
|
||||
|
||||
# TODO enable test-with-coverage in tavis
|
||||
.PHONY: travis-ci
|
||||
travis-ci: fmt lint vet embedmd test test-386
|
||||
travis-ci: imports lint vet embedmd test test-386
|
||||
|
||||
all-pkgs:
|
||||
@echo $(ALL_PKGS) | tr ' ' '\n' | sort
|
||||
|
@ -44,15 +44,15 @@ test-386:
|
|||
test-with-coverage:
|
||||
$(GOTEST) $(GOTEST_OPT_WITH_COVERAGE) $(ALL_PKGS)
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@FMTOUT=`$(GOFMT) -s -l $(ALL_SRC) 2>&1`; \
|
||||
if [ "$$FMTOUT" ]; then \
|
||||
echo "$(GOFMT) FAILED => gofmt the following files:\n"; \
|
||||
echo "$$FMTOUT\n"; \
|
||||
.PHONY: imports
|
||||
imports:
|
||||
@IMPORTSOUT=`$(GOIMPORTS) -l $(ALL_SRC) 2>&1`; \
|
||||
if [ "$$IMPORTSOUT" ]; then \
|
||||
echo "$(GOIMPORTS) FAILED => goimports the following files:\n"; \
|
||||
echo "$$IMPORTSOUT\n"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Fmt finished successfully"; \
|
||||
echo "Imports finished successfully"; \
|
||||
fi
|
||||
|
||||
.PHONY: lint
|
||||
|
@ -91,6 +91,7 @@ embedmd:
|
|||
|
||||
.PHONY: install-tools
|
||||
install-tools:
|
||||
go get -u golang.org/x/tools/cmd/cover
|
||||
go get -u golang.org/x/lint/golint
|
||||
go get -u golang.org/x/tools/cmd/cover
|
||||
go get -u golang.org/x/tools/cmd/goimports
|
||||
go get -u github.com/rakyll/embedmd
|
||||
|
|
|
@ -16,8 +16,8 @@ package ocgrpc
|
|||
|
||||
import (
|
||||
"context"
|
||||
"go.opencensus.io/trace"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/grpc/stats"
|
||||
)
|
||||
|
||||
|
|
|
@ -16,9 +16,10 @@ package ocgrpc
|
|||
|
||||
import (
|
||||
"context"
|
||||
"go.opencensus.io/trace"
|
||||
|
||||
"google.golang.org/grpc/stats"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
)
|
||||
|
||||
// ServerHandler implements gRPC stats.Handler recording OpenCensus stats and
|
||||
|
|
|
@ -15,16 +15,16 @@
|
|||
package ocgrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
"context"
|
||||
"go.opencensus.io/trace"
|
||||
"go.opencensus.io/trace/propagation"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/stats"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"go.opencensus.io/trace"
|
||||
"go.opencensus.io/trace/propagation"
|
||||
)
|
||||
|
||||
const traceContextKey = "grpc-trace-bin"
|
||||
|
|
|
@ -68,7 +68,7 @@ func ParseTraceID(tid string) (trace.TraceID, bool) {
|
|||
return trace.TraceID{}, false
|
||||
}
|
||||
b, err := hex.DecodeString(tid)
|
||||
if err != nil {
|
||||
if err != nil || len(b) > 16 {
|
||||
return trace.TraceID{}, false
|
||||
}
|
||||
var traceID trace.TraceID
|
||||
|
@ -90,7 +90,7 @@ func ParseSpanID(sid string) (spanID trace.SpanID, ok bool) {
|
|||
return trace.SpanID{}, false
|
||||
}
|
||||
b, err := hex.DecodeString(sid)
|
||||
if err != nil {
|
||||
if err != nil || len(b) > 8 {
|
||||
return trace.SpanID{}, false
|
||||
}
|
||||
start := 8 - len(b)
|
||||
|
|
|
@ -31,10 +31,19 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// Recorder provides an interface for exporting measurement information from
|
||||
// the static Record method by using the WithRecorder option.
|
||||
type Recorder interface {
|
||||
// Record records a set of measurements associated with the given tags and attachments.
|
||||
// The second argument is a `[]Measurement`.
|
||||
Record(*tag.Map, interface{}, map[string]interface{})
|
||||
}
|
||||
|
||||
type recordOptions struct {
|
||||
attachments metricdata.Attachments
|
||||
mutators []tag.Mutator
|
||||
measurements []Measurement
|
||||
recorder Recorder
|
||||
}
|
||||
|
||||
// WithAttachments applies provided exemplar attachments.
|
||||
|
@ -58,6 +67,14 @@ func WithMeasurements(measurements ...Measurement) Options {
|
|||
}
|
||||
}
|
||||
|
||||
// WithRecorder records the measurements to the specified `Recorder`, rather
|
||||
// than to the global metrics recorder.
|
||||
func WithRecorder(meter Recorder) Options {
|
||||
return func(ro *recordOptions) {
|
||||
ro.recorder = meter
|
||||
}
|
||||
}
|
||||
|
||||
// Options apply changes to recordOptions.
|
||||
type Options func(*recordOptions)
|
||||
|
||||
|
@ -93,6 +110,9 @@ func RecordWithOptions(ctx context.Context, ros ...Options) error {
|
|||
return nil
|
||||
}
|
||||
recorder := internal.DefaultRecorder
|
||||
if o.recorder != nil {
|
||||
recorder = o.recorder.Record
|
||||
}
|
||||
if recorder == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -14,13 +14,6 @@
|
|||
|
||||
package view
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
exportersMu sync.RWMutex // guards exporters
|
||||
exporters = make(map[Exporter]struct{})
|
||||
)
|
||||
|
||||
// Exporter exports the collected records as view data.
|
||||
//
|
||||
// The ExportView method should return quickly; if an
|
||||
|
@ -43,16 +36,10 @@ type Exporter interface {
|
|||
//
|
||||
// Binaries can register exporters, libraries shouldn't register exporters.
|
||||
func RegisterExporter(e Exporter) {
|
||||
exportersMu.Lock()
|
||||
defer exportersMu.Unlock()
|
||||
|
||||
exporters[e] = struct{}{}
|
||||
defaultWorker.RegisterExporter(e)
|
||||
}
|
||||
|
||||
// UnregisterExporter unregisters an exporter.
|
||||
func UnregisterExporter(e Exporter) {
|
||||
exportersMu.Lock()
|
||||
defer exportersMu.Unlock()
|
||||
|
||||
delete(exporters, e)
|
||||
defaultWorker.UnregisterExporter(e)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package view
|
|||
import (
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/resource"
|
||||
|
||||
"go.opencensus.io/metric/metricdata"
|
||||
"go.opencensus.io/stats"
|
||||
)
|
||||
|
@ -125,7 +127,7 @@ func rowToTimeseries(v *viewInternal, row *Row, now time.Time, startTime time.Ti
|
|||
}
|
||||
}
|
||||
|
||||
func viewToMetric(v *viewInternal, now time.Time, startTime time.Time) *metricdata.Metric {
|
||||
func viewToMetric(v *viewInternal, r *resource.Resource, now time.Time, startTime time.Time) *metricdata.Metric {
|
||||
if v.metricDescriptor.Type == metricdata.TypeGaugeInt64 ||
|
||||
v.metricDescriptor.Type == metricdata.TypeGaugeFloat64 {
|
||||
startTime = time.Time{}
|
||||
|
@ -144,6 +146,7 @@ func viewToMetric(v *viewInternal, now time.Time, startTime time.Time) *metricda
|
|||
m := &metricdata.Metric{
|
||||
Descriptor: *v.metricDescriptor,
|
||||
TimeSeries: ts,
|
||||
Resource: r,
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/resource"
|
||||
|
||||
"go.opencensus.io/metric/metricdata"
|
||||
"go.opencensus.io/metric/metricproducer"
|
||||
"go.opencensus.io/stats"
|
||||
|
@ -28,7 +30,7 @@ import (
|
|||
)
|
||||
|
||||
func init() {
|
||||
defaultWorker = newWorker()
|
||||
defaultWorker = NewMeter().(*worker)
|
||||
go defaultWorker.start()
|
||||
internal.DefaultRecorder = record
|
||||
}
|
||||
|
@ -47,8 +49,69 @@ type worker struct {
|
|||
c chan command
|
||||
quit, done chan bool
|
||||
mu sync.RWMutex
|
||||
r *resource.Resource
|
||||
|
||||
exportersMu sync.RWMutex
|
||||
exporters map[Exporter]struct{}
|
||||
}
|
||||
|
||||
// Meter defines an interface which allows a single process to maintain
|
||||
// multiple sets of metrics exports (intended for the advanced case where a
|
||||
// single process wants to report metrics about multiple objects, such as
|
||||
// multiple databases or HTTP services).
|
||||
//
|
||||
// Note that this is an advanced use case, and the static functions in this
|
||||
// module should cover the common use cases.
|
||||
type Meter interface {
|
||||
stats.Recorder
|
||||
// Find returns a registered view associated with this name.
|
||||
// If no registered view is found, nil is returned.
|
||||
Find(name string) *View
|
||||
// Register begins collecting data for the given views.
|
||||
// Once a view is registered, it reports data to the registered exporters.
|
||||
Register(views ...*View) error
|
||||
// Unregister the given views. Data will not longer be exported for these views
|
||||
// after Unregister returns.
|
||||
// It is not necessary to unregister from views you expect to collect for the
|
||||
// duration of your program execution.
|
||||
Unregister(views ...*View)
|
||||
// SetReportingPeriod sets the interval between reporting aggregated views in
|
||||
// the program. If duration is less than or equal to zero, it enables the
|
||||
// default behavior.
|
||||
//
|
||||
// Note: each exporter makes different promises about what the lowest supported
|
||||
// duration is. For example, the Stackdriver exporter recommends a value no
|
||||
// lower than 1 minute. Consult each exporter per your needs.
|
||||
SetReportingPeriod(time.Duration)
|
||||
|
||||
// RegisterExporter registers an exporter.
|
||||
// Collected data will be reported via all the
|
||||
// registered exporters. Once you no longer
|
||||
// want data to be exported, invoke UnregisterExporter
|
||||
// with the previously registered exporter.
|
||||
//
|
||||
// Binaries can register exporters, libraries shouldn't register exporters.
|
||||
RegisterExporter(Exporter)
|
||||
// UnregisterExporter unregisters an exporter.
|
||||
UnregisterExporter(Exporter)
|
||||
// SetResource may be used to set the Resource associated with this registry.
|
||||
// This is intended to be used in cases where a single process exports metrics
|
||||
// for multiple Resources, typically in a multi-tenant situation.
|
||||
SetResource(*resource.Resource)
|
||||
|
||||
// Start causes the Meter to start processing Record calls and aggregating
|
||||
// statistics as well as exporting data.
|
||||
Start()
|
||||
// Stop causes the Meter to stop processing calls and terminate data export.
|
||||
Stop()
|
||||
|
||||
// RetrieveData gets a snapshot of the data collected for the the view registered
|
||||
// with the given name. It is intended for testing only.
|
||||
RetrieveData(viewName string) ([]*Row, error)
|
||||
}
|
||||
|
||||
var _ Meter = (*worker)(nil)
|
||||
|
||||
var defaultWorker *worker
|
||||
|
||||
var defaultReportingDuration = 10 * time.Second
|
||||
|
@ -56,11 +119,17 @@ var defaultReportingDuration = 10 * time.Second
|
|||
// Find returns a registered view associated with this name.
|
||||
// If no registered view is found, nil is returned.
|
||||
func Find(name string) (v *View) {
|
||||
return defaultWorker.Find(name)
|
||||
}
|
||||
|
||||
// Find returns a registered view associated with this name.
|
||||
// If no registered view is found, nil is returned.
|
||||
func (w *worker) Find(name string) (v *View) {
|
||||
req := &getViewByNameReq{
|
||||
name: name,
|
||||
c: make(chan *getViewByNameResp),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
w.c <- req
|
||||
resp := <-req.c
|
||||
return resp.v
|
||||
}
|
||||
|
@ -68,11 +137,17 @@ func Find(name string) (v *View) {
|
|||
// Register begins collecting data for the given views.
|
||||
// Once a view is registered, it reports data to the registered exporters.
|
||||
func Register(views ...*View) error {
|
||||
return defaultWorker.Register(views...)
|
||||
}
|
||||
|
||||
// Register begins collecting data for the given views.
|
||||
// Once a view is registered, it reports data to the registered exporters.
|
||||
func (w *worker) Register(views ...*View) error {
|
||||
req := ®isterViewReq{
|
||||
views: views,
|
||||
err: make(chan error),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
w.c <- req
|
||||
return <-req.err
|
||||
}
|
||||
|
||||
|
@ -81,6 +156,14 @@ func Register(views ...*View) error {
|
|||
// It is not necessary to unregister from views you expect to collect for the
|
||||
// duration of your program execution.
|
||||
func Unregister(views ...*View) {
|
||||
defaultWorker.Unregister(views...)
|
||||
}
|
||||
|
||||
// Unregister the given views. Data will not longer be exported for these views
|
||||
// after Unregister returns.
|
||||
// It is not necessary to unregister from views you expect to collect for the
|
||||
// duration of your program execution.
|
||||
func (w *worker) Unregister(views ...*View) {
|
||||
names := make([]string, len(views))
|
||||
for i := range views {
|
||||
names[i] = views[i].Name
|
||||
|
@ -89,31 +172,42 @@ func Unregister(views ...*View) {
|
|||
views: names,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
w.c <- req
|
||||
<-req.done
|
||||
}
|
||||
|
||||
// RetrieveData gets a snapshot of the data collected for the the view registered
|
||||
// with the given name. It is intended for testing only.
|
||||
func RetrieveData(viewName string) ([]*Row, error) {
|
||||
return defaultWorker.RetrieveData(viewName)
|
||||
}
|
||||
|
||||
// RetrieveData gets a snapshot of the data collected for the the view registered
|
||||
// with the given name. It is intended for testing only.
|
||||
func (w *worker) RetrieveData(viewName string) ([]*Row, error) {
|
||||
req := &retrieveDataReq{
|
||||
now: time.Now(),
|
||||
v: viewName,
|
||||
c: make(chan *retrieveDataResp),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
w.c <- req
|
||||
resp := <-req.c
|
||||
return resp.rows, resp.err
|
||||
}
|
||||
|
||||
func record(tags *tag.Map, ms interface{}, attachments map[string]interface{}) {
|
||||
defaultWorker.Record(tags, ms, attachments)
|
||||
}
|
||||
|
||||
// Record records a set of measurements ms associated with the given tags and attachments.
|
||||
func (w *worker) Record(tags *tag.Map, ms interface{}, attachments map[string]interface{}) {
|
||||
req := &recordReq{
|
||||
tm: tags,
|
||||
ms: ms.([]stats.Measurement),
|
||||
attachments: attachments,
|
||||
t: time.Now(),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
w.c <- req
|
||||
}
|
||||
|
||||
// SetReportingPeriod sets the interval between reporting aggregated views in
|
||||
|
@ -124,17 +218,31 @@ func record(tags *tag.Map, ms interface{}, attachments map[string]interface{}) {
|
|||
// duration is. For example, the Stackdriver exporter recommends a value no
|
||||
// lower than 1 minute. Consult each exporter per your needs.
|
||||
func SetReportingPeriod(d time.Duration) {
|
||||
defaultWorker.SetReportingPeriod(d)
|
||||
}
|
||||
|
||||
// SetReportingPeriod sets the interval between reporting aggregated views in
|
||||
// the program. If duration is less than or equal to zero, it enables the
|
||||
// default behavior.
|
||||
//
|
||||
// Note: each exporter makes different promises about what the lowest supported
|
||||
// duration is. For example, the Stackdriver exporter recommends a value no
|
||||
// lower than 1 minute. Consult each exporter per your needs.
|
||||
func (w *worker) SetReportingPeriod(d time.Duration) {
|
||||
// TODO(acetechnologist): ensure that the duration d is more than a certain
|
||||
// value. e.g. 1s
|
||||
req := &setReportingPeriodReq{
|
||||
d: d,
|
||||
c: make(chan bool),
|
||||
}
|
||||
defaultWorker.c <- req
|
||||
w.c <- req
|
||||
<-req.c // don't return until the timer is set to the new duration.
|
||||
}
|
||||
|
||||
func newWorker() *worker {
|
||||
// NewMeter constructs a Meter instance. You should only need to use this if
|
||||
// you need to separate out Measurement recordings and View aggregations within
|
||||
// a single process.
|
||||
func NewMeter() Meter {
|
||||
return &worker{
|
||||
measures: make(map[string]*measureRef),
|
||||
views: make(map[string]*viewInternal),
|
||||
|
@ -143,9 +251,23 @@ func newWorker() *worker {
|
|||
c: make(chan command, 1024),
|
||||
quit: make(chan bool),
|
||||
done: make(chan bool),
|
||||
|
||||
exporters: make(map[Exporter]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// SetResource associates all data collected by this Meter with the specified
|
||||
// resource. This resource is reported when using metricexport.ReadAndExport;
|
||||
// it is not provided when used with ExportView/RegisterExporter, because that
|
||||
// interface does not provide a means for reporting the Resource.
|
||||
func (w *worker) SetResource(r *resource.Resource) {
|
||||
w.r = r
|
||||
}
|
||||
|
||||
func (w *worker) Start() {
|
||||
go w.start()
|
||||
}
|
||||
|
||||
func (w *worker) start() {
|
||||
prodMgr := metricproducer.GlobalManager()
|
||||
prodMgr.AddProducer(w)
|
||||
|
@ -155,7 +277,7 @@ func (w *worker) start() {
|
|||
case cmd := <-w.c:
|
||||
cmd.handleCommand(w)
|
||||
case <-w.timer.C:
|
||||
w.reportUsage(time.Now())
|
||||
w.reportUsage()
|
||||
case <-w.quit:
|
||||
w.timer.Stop()
|
||||
close(w.c)
|
||||
|
@ -165,7 +287,7 @@ func (w *worker) start() {
|
|||
}
|
||||
}
|
||||
|
||||
func (w *worker) stop() {
|
||||
func (w *worker) Stop() {
|
||||
prodMgr := metricproducer.GlobalManager()
|
||||
prodMgr.DeleteProducer(w)
|
||||
|
||||
|
@ -202,44 +324,45 @@ func (w *worker) tryRegisterView(v *View) (*viewInternal, error) {
|
|||
return x, nil
|
||||
}
|
||||
w.views[vi.view.Name] = vi
|
||||
w.startTimes[vi] = time.Now()
|
||||
ref := w.getMeasureRef(vi.view.Measure.Name())
|
||||
ref.views[vi] = struct{}{}
|
||||
return vi, nil
|
||||
}
|
||||
|
||||
func (w *worker) unregisterView(viewName string) {
|
||||
func (w *worker) unregisterView(v *viewInternal) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
delete(w.views, viewName)
|
||||
delete(w.views, v.view.Name)
|
||||
delete(w.startTimes, v)
|
||||
if measure := w.measures[v.view.Measure.Name()]; measure != nil {
|
||||
delete(measure.views, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *worker) reportView(v *viewInternal, now time.Time) {
|
||||
func (w *worker) reportView(v *viewInternal) {
|
||||
if !v.isSubscribed() {
|
||||
return
|
||||
}
|
||||
rows := v.collectedRows()
|
||||
_, ok := w.startTimes[v]
|
||||
if !ok {
|
||||
w.startTimes[v] = now
|
||||
}
|
||||
viewData := &Data{
|
||||
View: v.view,
|
||||
Start: w.startTimes[v],
|
||||
End: time.Now(),
|
||||
Rows: rows,
|
||||
}
|
||||
exportersMu.Lock()
|
||||
for e := range exporters {
|
||||
w.exportersMu.Lock()
|
||||
defer w.exportersMu.Unlock()
|
||||
for e := range w.exporters {
|
||||
e.ExportView(viewData)
|
||||
}
|
||||
exportersMu.Unlock()
|
||||
}
|
||||
|
||||
func (w *worker) reportUsage(now time.Time) {
|
||||
func (w *worker) reportUsage() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
for _, v := range w.views {
|
||||
w.reportView(v, now)
|
||||
w.reportView(v)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,11 +371,6 @@ func (w *worker) toMetric(v *viewInternal, now time.Time) *metricdata.Metric {
|
|||
return nil
|
||||
}
|
||||
|
||||
_, ok := w.startTimes[v]
|
||||
if !ok {
|
||||
w.startTimes[v] = now
|
||||
}
|
||||
|
||||
var startTime time.Time
|
||||
if v.metricDescriptor.Type == metricdata.TypeGaugeInt64 ||
|
||||
v.metricDescriptor.Type == metricdata.TypeGaugeFloat64 {
|
||||
|
@ -261,7 +379,7 @@ func (w *worker) toMetric(v *viewInternal, now time.Time) *metricdata.Metric {
|
|||
startTime = w.startTimes[v]
|
||||
}
|
||||
|
||||
return viewToMetric(v, now, startTime)
|
||||
return viewToMetric(v, w.r, now, startTime)
|
||||
}
|
||||
|
||||
// Read reads all view data and returns them as metrics.
|
||||
|
@ -279,3 +397,17 @@ func (w *worker) Read() []*metricdata.Metric {
|
|||
}
|
||||
return metrics
|
||||
}
|
||||
|
||||
func (w *worker) RegisterExporter(e Exporter) {
|
||||
w.exportersMu.Lock()
|
||||
defer w.exportersMu.Unlock()
|
||||
|
||||
w.exporters[e] = struct{}{}
|
||||
}
|
||||
|
||||
func (w *worker) UnregisterExporter(e Exporter) {
|
||||
w.exportersMu.Lock()
|
||||
defer w.exportersMu.Unlock()
|
||||
|
||||
delete(w.exporters, e)
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ func (cmd *unregisterFromViewReq) handleCommand(w *worker) {
|
|||
}
|
||||
|
||||
// Report pending data for this view before removing it.
|
||||
w.reportView(vi, time.Now())
|
||||
w.reportView(vi)
|
||||
|
||||
vi.unsubscribe()
|
||||
if !vi.isSubscribed() {
|
||||
|
@ -103,7 +103,7 @@ func (cmd *unregisterFromViewReq) handleCommand(w *worker) {
|
|||
// The collected data can be cleared.
|
||||
vi.clearRows()
|
||||
}
|
||||
w.unregisterView(name)
|
||||
w.unregisterView(vi)
|
||||
}
|
||||
cmd.done <- struct{}{}
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ func (cmd *recordReq) handleCommand(w *worker) {
|
|||
}
|
||||
ref := w.getMeasureRef(m.Measure().Name())
|
||||
for v := range ref.views {
|
||||
v.addSample(cmd.tm, m.Value(), cmd.attachments, time.Now())
|
||||
v.addSample(cmd.tm, m.Value(), cmd.attachments, cmd.t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func (lm lruMap) len() int {
|
|||
}
|
||||
|
||||
func (lm lruMap) keys() []interface{} {
|
||||
keys := []interface{}{}
|
||||
keys := make([]interface{}, len(lm.cacheKeys))
|
||||
for k := range lm.cacheKeys {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
|
|
@ -345,7 +345,7 @@ func (s *Span) SetStatus(status Status) {
|
|||
}
|
||||
|
||||
func (s *Span) interfaceArrayToLinksArray() []Link {
|
||||
linksArr := make([]Link, 0)
|
||||
linksArr := make([]Link, 0, len(s.links.queue))
|
||||
for _, value := range s.links.queue {
|
||||
linksArr = append(linksArr, value.(Link))
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ func (s *Span) interfaceArrayToLinksArray() []Link {
|
|||
}
|
||||
|
||||
func (s *Span) interfaceArrayToMessageEventArray() []MessageEvent {
|
||||
messageEventArr := make([]MessageEvent, 0)
|
||||
messageEventArr := make([]MessageEvent, 0, len(s.messageEvents.queue))
|
||||
for _, value := range s.messageEvents.queue {
|
||||
messageEventArr = append(messageEventArr, value.(MessageEvent))
|
||||
}
|
||||
|
@ -361,7 +361,7 @@ func (s *Span) interfaceArrayToMessageEventArray() []MessageEvent {
|
|||
}
|
||||
|
||||
func (s *Span) interfaceArrayToAnnotationArray() []Annotation {
|
||||
annotationArr := make([]Annotation, 0)
|
||||
annotationArr := make([]Annotation, 0, len(s.annotations.queue))
|
||||
for _, value := range s.annotations.queue {
|
||||
annotationArr = append(annotationArr, value.(Annotation))
|
||||
}
|
||||
|
@ -369,7 +369,7 @@ func (s *Span) interfaceArrayToAnnotationArray() []Annotation {
|
|||
}
|
||||
|
||||
func (s *Span) lruAttributesToAttributeMap() map[string]interface{} {
|
||||
attributes := make(map[string]interface{})
|
||||
attributes := make(map[string]interface{}, s.lruAttributes.len())
|
||||
for _, key := range s.lruAttributes.keys() {
|
||||
value, ok := s.lruAttributes.get(key)
|
||||
if ok {
|
||||
|
@ -420,7 +420,7 @@ func (s *Span) lazyPrintfInternal(attributes []Attribute, format string, a ...in
|
|||
var m map[string]interface{}
|
||||
s.mu.Lock()
|
||||
if len(attributes) != 0 {
|
||||
m = make(map[string]interface{})
|
||||
m = make(map[string]interface{}, len(attributes))
|
||||
copyAttributes(m, attributes)
|
||||
}
|
||||
s.annotations.add(Annotation{
|
||||
|
@ -436,7 +436,7 @@ func (s *Span) printStringInternal(attributes []Attribute, str string) {
|
|||
var a map[string]interface{}
|
||||
s.mu.Lock()
|
||||
if len(attributes) != 0 {
|
||||
a = make(map[string]interface{})
|
||||
a = make(map[string]interface{}, len(attributes))
|
||||
copyAttributes(a, attributes)
|
||||
}
|
||||
s.annotations.add(Annotation{
|
||||
|
|
|
@ -0,0 +1,495 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package h2c implements the unencrypted "h2c" form of HTTP/2.
|
||||
//
|
||||
// The h2c protocol is the non-TLS version of HTTP/2 which is not available from
|
||||
// net/http or golang.org/x/net/http2.
|
||||
package h2c
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
var (
|
||||
http2VerboseLogs bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
e := os.Getenv("GODEBUG")
|
||||
if strings.Contains(e, "http2debug=1") || strings.Contains(e, "http2debug=2") {
|
||||
http2VerboseLogs = true
|
||||
}
|
||||
}
|
||||
|
||||
// h2cHandler is a Handler which implements h2c by hijacking the HTTP/1 traffic
|
||||
// that should be h2c traffic. There are two ways to begin a h2c connection
|
||||
// (RFC 7540 Section 3.2 and 3.4): (1) Starting with Prior Knowledge - this
|
||||
// works by starting an h2c connection with a string of bytes that is valid
|
||||
// HTTP/1, but unlikely to occur in practice and (2) Upgrading from HTTP/1 to
|
||||
// h2c - this works by using the HTTP/1 Upgrade header to request an upgrade to
|
||||
// h2c. When either of those situations occur we hijack the HTTP/1 connection,
|
||||
// convert it to a HTTP/2 connection and pass the net.Conn to http2.ServeConn.
|
||||
type h2cHandler struct {
|
||||
Handler http.Handler
|
||||
s *http2.Server
|
||||
}
|
||||
|
||||
// NewHandler returns an http.Handler that wraps h, intercepting any h2c
|
||||
// traffic. If a request is an h2c connection, it's hijacked and redirected to
|
||||
// s.ServeConn. Otherwise the returned Handler just forwards requests to h. This
|
||||
// works because h2c is designed to be parseable as valid HTTP/1, but ignored by
|
||||
// any HTTP server that does not handle h2c. Therefore we leverage the HTTP/1
|
||||
// compatible parts of the Go http library to parse and recognize h2c requests.
|
||||
// Once a request is recognized as h2c, we hijack the connection and convert it
|
||||
// to an HTTP/2 connection which is understandable to s.ServeConn. (s.ServeConn
|
||||
// understands HTTP/2 except for the h2c part of it.)
|
||||
func NewHandler(h http.Handler, s *http2.Server) http.Handler {
|
||||
return &h2cHandler{
|
||||
Handler: h,
|
||||
s: s,
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTP implement the h2c support that is enabled by h2c.GetH2CHandler.
|
||||
func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Handle h2c with prior knowledge (RFC 7540 Section 3.4)
|
||||
if r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" {
|
||||
if http2VerboseLogs {
|
||||
log.Print("h2c: attempting h2c with prior knowledge.")
|
||||
}
|
||||
conn, err := initH2CWithPriorKnowledge(w)
|
||||
if err != nil {
|
||||
if http2VerboseLogs {
|
||||
log.Printf("h2c: error h2c with prior knowledge: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
s.s.ServeConn(conn, &http2.ServeConnOpts{Handler: s.Handler})
|
||||
return
|
||||
}
|
||||
// Handle Upgrade to h2c (RFC 7540 Section 3.2)
|
||||
if conn, err := h2cUpgrade(w, r); err == nil {
|
||||
defer conn.Close()
|
||||
|
||||
s.s.ServeConn(conn, &http2.ServeConnOpts{Handler: s.Handler})
|
||||
return
|
||||
}
|
||||
|
||||
s.Handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// initH2CWithPriorKnowledge implements creating a h2c connection with prior
|
||||
// knowledge (Section 3.4) and creates a net.Conn suitable for http2.ServeConn.
|
||||
// All we have to do is look for the client preface that is suppose to be part
|
||||
// of the body, and reforward the client preface on the net.Conn this function
|
||||
// creates.
|
||||
func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) {
|
||||
hijacker, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
panic("Hijack not supported.")
|
||||
}
|
||||
conn, rw, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Hijack failed: %v", err))
|
||||
}
|
||||
|
||||
const expectedBody = "SM\r\n\r\n"
|
||||
|
||||
buf := make([]byte, len(expectedBody))
|
||||
n, err := io.ReadFull(rw, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read from the buffer: %s", err)
|
||||
}
|
||||
|
||||
if string(buf[:n]) == expectedBody {
|
||||
c := &rwConn{
|
||||
Conn: conn,
|
||||
Reader: io.MultiReader(strings.NewReader(http2.ClientPreface), rw),
|
||||
BufWriter: rw.Writer,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
if http2VerboseLogs {
|
||||
log.Printf(
|
||||
"h2c: missing the request body portion of the client preface. Wanted: %v Got: %v",
|
||||
[]byte(expectedBody),
|
||||
buf[0:n],
|
||||
)
|
||||
}
|
||||
return nil, errors.New("invalid client preface")
|
||||
}
|
||||
|
||||
// drainClientPreface reads a single instance of the HTTP/2 client preface from
|
||||
// the supplied reader.
|
||||
func drainClientPreface(r io.Reader) error {
|
||||
var buf bytes.Buffer
|
||||
prefaceLen := int64(len(http2.ClientPreface))
|
||||
n, err := io.CopyN(&buf, r, prefaceLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != prefaceLen || buf.String() != http2.ClientPreface {
|
||||
return fmt.Errorf("Client never sent: %s", http2.ClientPreface)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// h2cUpgrade establishes a h2c connection using the HTTP/1 upgrade (Section 3.2).
|
||||
func h2cUpgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) {
|
||||
if !isH2CUpgrade(r.Header) {
|
||||
return nil, errors.New("non-conforming h2c headers")
|
||||
}
|
||||
|
||||
// Initial bytes we put into conn to fool http2 server
|
||||
initBytes, _, err := convertH1ReqToH2(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hijacker, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, errors.New("hijack not supported.")
|
||||
}
|
||||
conn, rw, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hijack failed: %v", err)
|
||||
}
|
||||
|
||||
rw.Write([]byte("HTTP/1.1 101 Switching Protocols\r\n" +
|
||||
"Connection: Upgrade\r\n" +
|
||||
"Upgrade: h2c\r\n\r\n"))
|
||||
rw.Flush()
|
||||
|
||||
// A conforming client will now send an H2 client preface which need to drain
|
||||
// since we already sent this.
|
||||
if err := drainClientPreface(rw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &rwConn{
|
||||
Conn: conn,
|
||||
Reader: io.MultiReader(initBytes, rw),
|
||||
BufWriter: newSettingsAckSwallowWriter(rw.Writer),
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// convert the data contained in the HTTP/1 upgrade request into the HTTP/2
|
||||
// version in byte form.
|
||||
func convertH1ReqToH2(r *http.Request) (*bytes.Buffer, []http2.Setting, error) {
|
||||
h2Bytes := bytes.NewBuffer([]byte((http2.ClientPreface)))
|
||||
framer := http2.NewFramer(h2Bytes, nil)
|
||||
settings, err := getH2Settings(r.Header)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := framer.WriteSettings(settings...); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
headerBytes, err := getH2HeaderBytes(r, getMaxHeaderTableSize(settings))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
maxFrameSize := int(getMaxFrameSize(settings))
|
||||
needOneHeader := len(headerBytes) < maxFrameSize
|
||||
err = framer.WriteHeaders(http2.HeadersFrameParam{
|
||||
StreamID: 1,
|
||||
BlockFragment: headerBytes,
|
||||
EndHeaders: needOneHeader,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i := maxFrameSize; i < len(headerBytes); i += maxFrameSize {
|
||||
if len(headerBytes)-i > maxFrameSize {
|
||||
if err := framer.WriteContinuation(1,
|
||||
false, // endHeaders
|
||||
headerBytes[i:maxFrameSize]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
if err := framer.WriteContinuation(1,
|
||||
true, // endHeaders
|
||||
headerBytes[i:]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return h2Bytes, settings, nil
|
||||
}
|
||||
|
||||
// getMaxFrameSize returns the SETTINGS_MAX_FRAME_SIZE. If not present default
|
||||
// value is 16384 as specified by RFC 7540 Section 6.5.2.
|
||||
func getMaxFrameSize(settings []http2.Setting) uint32 {
|
||||
for _, setting := range settings {
|
||||
if setting.ID == http2.SettingMaxFrameSize {
|
||||
return setting.Val
|
||||
}
|
||||
}
|
||||
return 16384
|
||||
}
|
||||
|
||||
// getMaxHeaderTableSize returns the SETTINGS_HEADER_TABLE_SIZE. If not present
|
||||
// default value is 4096 as specified by RFC 7540 Section 6.5.2.
|
||||
func getMaxHeaderTableSize(settings []http2.Setting) uint32 {
|
||||
for _, setting := range settings {
|
||||
if setting.ID == http2.SettingHeaderTableSize {
|
||||
return setting.Val
|
||||
}
|
||||
}
|
||||
return 4096
|
||||
}
|
||||
|
||||
// bufWriter is a Writer interface that also has a Flush method.
|
||||
type bufWriter interface {
|
||||
io.Writer
|
||||
Flush() error
|
||||
}
|
||||
|
||||
// rwConn implements net.Conn but overrides Read and Write so that reads and
|
||||
// writes are forwarded to the provided io.Reader and bufWriter.
|
||||
type rwConn struct {
|
||||
net.Conn
|
||||
io.Reader
|
||||
BufWriter bufWriter
|
||||
}
|
||||
|
||||
// Read forwards reads to the underlying Reader.
|
||||
func (c *rwConn) Read(p []byte) (int, error) {
|
||||
return c.Reader.Read(p)
|
||||
}
|
||||
|
||||
// Write forwards writes to the underlying bufWriter and immediately flushes.
|
||||
func (c *rwConn) Write(p []byte) (int, error) {
|
||||
n, err := c.BufWriter.Write(p)
|
||||
if err := c.BufWriter.Flush(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// settingsAckSwallowWriter is a writer that normally forwards bytes to its
|
||||
// underlying Writer, but swallows the first SettingsAck frame that it sees.
|
||||
type settingsAckSwallowWriter struct {
|
||||
Writer *bufio.Writer
|
||||
buf []byte
|
||||
didSwallow bool
|
||||
}
|
||||
|
||||
// newSettingsAckSwallowWriter returns a new settingsAckSwallowWriter.
|
||||
func newSettingsAckSwallowWriter(w *bufio.Writer) *settingsAckSwallowWriter {
|
||||
return &settingsAckSwallowWriter{
|
||||
Writer: w,
|
||||
buf: make([]byte, 0),
|
||||
didSwallow: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements io.Writer interface. Normally forwards bytes to w.Writer,
|
||||
// except for the first Settings ACK frame that it sees.
|
||||
func (w *settingsAckSwallowWriter) Write(p []byte) (int, error) {
|
||||
if !w.didSwallow {
|
||||
w.buf = append(w.buf, p...)
|
||||
// Process all the frames we have collected into w.buf
|
||||
for {
|
||||
// Append until we get full frame header which is 9 bytes
|
||||
if len(w.buf) < 9 {
|
||||
break
|
||||
}
|
||||
// Check if we have collected a whole frame.
|
||||
fh, err := http2.ReadFrameHeader(bytes.NewBuffer(w.buf))
|
||||
if err != nil {
|
||||
// Corrupted frame, fail current Write
|
||||
return 0, err
|
||||
}
|
||||
fSize := fh.Length + 9
|
||||
if uint32(len(w.buf)) < fSize {
|
||||
// Have not collected whole frame. Stop processing buf, and withold on
|
||||
// forward bytes to w.Writer until we get the full frame.
|
||||
break
|
||||
}
|
||||
|
||||
// We have now collected a whole frame.
|
||||
if fh.Type == http2.FrameSettings && fh.Flags.Has(http2.FlagSettingsAck) {
|
||||
// If Settings ACK frame, do not forward to underlying writer, remove
|
||||
// bytes from w.buf, and record that we have swallowed Settings Ack
|
||||
// frame.
|
||||
w.didSwallow = true
|
||||
w.buf = w.buf[fSize:]
|
||||
continue
|
||||
}
|
||||
|
||||
// Not settings ack frame. Forward bytes to w.Writer.
|
||||
if _, err := w.Writer.Write(w.buf[:fSize]); err != nil {
|
||||
// Couldn't forward bytes. Fail current Write.
|
||||
return 0, err
|
||||
}
|
||||
w.buf = w.buf[fSize:]
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
return w.Writer.Write(p)
|
||||
}
|
||||
|
||||
// Flush calls w.Writer.Flush.
|
||||
func (w *settingsAckSwallowWriter) Flush() error {
|
||||
return w.Writer.Flush()
|
||||
}
|
||||
|
||||
// isH2CUpgrade returns true if the header properly request an upgrade to h2c
|
||||
// as specified by Section 3.2.
|
||||
func isH2CUpgrade(h http.Header) bool {
|
||||
return httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Upgrade")], "h2c") &&
|
||||
httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Connection")], "HTTP2-Settings")
|
||||
}
|
||||
|
||||
// getH2Settings returns the []http2.Setting that are encoded in the
|
||||
// HTTP2-Settings header.
|
||||
func getH2Settings(h http.Header) ([]http2.Setting, error) {
|
||||
vals, ok := h[textproto.CanonicalMIMEHeaderKey("HTTP2-Settings")]
|
||||
if !ok {
|
||||
return nil, errors.New("missing HTTP2-Settings header")
|
||||
}
|
||||
if len(vals) != 1 {
|
||||
return nil, fmt.Errorf("expected 1 HTTP2-Settings. Got: %v", vals)
|
||||
}
|
||||
settings, err := decodeSettings(vals[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid HTTP2-Settings: %q", vals[0])
|
||||
}
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
// decodeSettings decodes the base64url header value of the HTTP2-Settings
|
||||
// header. RFC 7540 Section 3.2.1.
|
||||
func decodeSettings(headerVal string) ([]http2.Setting, error) {
|
||||
b, err := base64.RawURLEncoding.DecodeString(headerVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(b)%6 != 0 {
|
||||
return nil, err
|
||||
}
|
||||
settings := make([]http2.Setting, 0)
|
||||
for i := 0; i < len(b)/6; i++ {
|
||||
settings = append(settings, http2.Setting{
|
||||
ID: http2.SettingID(binary.BigEndian.Uint16(b[i*6 : i*6+2])),
|
||||
Val: binary.BigEndian.Uint32(b[i*6+2 : i*6+6]),
|
||||
})
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
// getH2HeaderBytes return the headers in r a []bytes encoded by HPACK.
|
||||
func getH2HeaderBytes(r *http.Request, maxHeaderTableSize uint32) ([]byte, error) {
|
||||
headerBytes := bytes.NewBuffer(nil)
|
||||
hpackEnc := hpack.NewEncoder(headerBytes)
|
||||
hpackEnc.SetMaxDynamicTableSize(maxHeaderTableSize)
|
||||
|
||||
// Section 8.1.2.3
|
||||
err := hpackEnc.WriteField(hpack.HeaderField{
|
||||
Name: ":method",
|
||||
Value: r.Method,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = hpackEnc.WriteField(hpack.HeaderField{
|
||||
Name: ":scheme",
|
||||
Value: "http",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = hpackEnc.WriteField(hpack.HeaderField{
|
||||
Name: ":authority",
|
||||
Value: r.Host,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path := r.URL.Path
|
||||
if r.URL.RawQuery != "" {
|
||||
path = strings.Join([]string{path, r.URL.RawQuery}, "?")
|
||||
}
|
||||
err = hpackEnc.WriteField(hpack.HeaderField{
|
||||
Name: ":path",
|
||||
Value: path,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO Implement Section 8.3
|
||||
|
||||
for header, values := range r.Header {
|
||||
// Skip non h2 headers
|
||||
if isNonH2Header(header) {
|
||||
continue
|
||||
}
|
||||
for _, v := range values {
|
||||
err := hpackEnc.WriteField(hpack.HeaderField{
|
||||
Name: strings.ToLower(header),
|
||||
Value: v,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return headerBytes.Bytes(), nil
|
||||
}
|
||||
|
||||
// Connection specific headers listed in RFC 7540 Section 8.1.2.2 that are not
|
||||
// suppose to be transferred to HTTP/2. The Http2-Settings header is skipped
|
||||
// since already use to create the HTTP/2 SETTINGS frame.
|
||||
var nonH2Headers = []string{
|
||||
"Connection",
|
||||
"Keep-Alive",
|
||||
"Proxy-Connection",
|
||||
"Transfer-Encoding",
|
||||
"Upgrade",
|
||||
"Http2-Settings",
|
||||
}
|
||||
|
||||
// isNonH2Header returns true if header should not be transferred to HTTP/2.
|
||||
func isNonH2Header(header string) bool {
|
||||
for _, nonH2h := range nonH2Headers {
|
||||
if header == nonH2h {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -47,8 +47,8 @@ func (a ByPath) Len() int { return len(a) }
|
|||
func (a ByPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByPath) Less(i, j int) bool { return a[i].Path < a[j].Path }
|
||||
|
||||
func NewPatch(operation, path string, value interface{}) Operation {
|
||||
return Operation{Operation: operation, Path: path, Value: value}
|
||||
func NewOperation(op, path string, value interface{}) Operation {
|
||||
return Operation{Operation: op, Path: path, Value: value}
|
||||
}
|
||||
|
||||
// CreatePatch creates a patch as specified in http://jsonpatch.com/
|
||||
|
@ -162,7 +162,7 @@ func diff(a, b map[string]interface{}, path string, patch []Operation) ([]Operat
|
|||
av, ok := a[key]
|
||||
// value was added
|
||||
if !ok {
|
||||
patch = append(patch, NewPatch("add", p, bv))
|
||||
patch = append(patch, NewOperation("add", p, bv))
|
||||
continue
|
||||
}
|
||||
// Types are the same, compare values
|
||||
|
@ -178,7 +178,7 @@ func diff(a, b map[string]interface{}, path string, patch []Operation) ([]Operat
|
|||
if !found {
|
||||
p := makePath(path, key)
|
||||
|
||||
patch = append(patch, NewPatch("remove", p, nil))
|
||||
patch = append(patch, NewOperation("remove", p, nil))
|
||||
}
|
||||
}
|
||||
return patch, nil
|
||||
|
@ -192,10 +192,10 @@ func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation,
|
|||
// do nothing
|
||||
return patch, nil
|
||||
} else if at == nil && bt != nil {
|
||||
return append(patch, NewPatch("add", p, bv)), nil
|
||||
return append(patch, NewOperation("add", p, bv)), nil
|
||||
} else if at != bt {
|
||||
// If types have changed, replace completely (preserves null in destination)
|
||||
return append(patch, NewPatch("replace", p, bv)), nil
|
||||
return append(patch, NewOperation("replace", p, bv)), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation,
|
|||
}
|
||||
case string, float64, bool:
|
||||
if !matchesValue(av, bv) {
|
||||
patch = append(patch, NewPatch("replace", p, bv))
|
||||
patch = append(patch, NewOperation("replace", p, bv))
|
||||
}
|
||||
case []interface{}:
|
||||
bt := bv.([]interface{})
|
||||
|
@ -218,10 +218,10 @@ func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation,
|
|||
} else {
|
||||
n := min(len(at), len(bt))
|
||||
for i := len(at) - 1; i >= n; i-- {
|
||||
patch = append(patch, NewPatch("remove", makePath(p, i), nil))
|
||||
patch = append(patch, NewOperation("remove", makePath(p, i), nil))
|
||||
}
|
||||
for i := n; i < len(bt); i++ {
|
||||
patch = append(patch, NewPatch("add", makePath(p, i), bt[i]))
|
||||
patch = append(patch, NewOperation("add", makePath(p, i), bt[i]))
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
var err error
|
||||
|
@ -313,16 +313,16 @@ func min(x int, y int) int {
|
|||
|
||||
func backtrace(s, t []interface{}, p string, i int, j int, matrix [][]int) []Operation {
|
||||
if i > 0 && matrix[i-1][j]+1 == matrix[i][j] {
|
||||
op := NewPatch("remove", makePath(p, i-1), nil)
|
||||
op := NewOperation("remove", makePath(p, i-1), nil)
|
||||
return append([]Operation{op}, backtrace(s, t, p, i-1, j, matrix)...)
|
||||
}
|
||||
if j > 0 && matrix[i][j-1]+1 == matrix[i][j] {
|
||||
op := NewPatch("add", makePath(p, i), t[j-1])
|
||||
op := NewOperation("add", makePath(p, i), t[j-1])
|
||||
return append([]Operation{op}, backtrace(s, t, p, i, j-1, matrix)...)
|
||||
}
|
||||
if i > 0 && j > 0 && matrix[i-1][j-1]+1 == matrix[i][j] {
|
||||
if isBasicType(s[0]) {
|
||||
op := NewPatch("replace", makePath(p, i-1), t[j-1])
|
||||
op := NewOperation("replace", makePath(p, i-1), t[j-1])
|
||||
return append([]Operation{op}, backtrace(s, t, p, i-1, j-1, matrix)...)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
func NewUUID() types.UID {
|
||||
return types.UID(uuid.New().String())
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package leaderelection
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HealthzAdaptor associates the /healthz endpoint with the LeaderElection object.
|
||||
// It helps deal with the /healthz endpoint being set up prior to the LeaderElection.
|
||||
// This contains the code needed to act as an adaptor between the leader
|
||||
// election code the health check code. It allows us to provide health
|
||||
// status about the leader election. Most specifically about if the leader
|
||||
// has failed to renew without exiting the process. In that case we should
|
||||
// report not healthy and rely on the kubelet to take down the process.
|
||||
type HealthzAdaptor struct {
|
||||
pointerLock sync.Mutex
|
||||
le *LeaderElector
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// Name returns the name of the health check we are implementing.
|
||||
func (l *HealthzAdaptor) Name() string {
|
||||
return "leaderElection"
|
||||
}
|
||||
|
||||
// Check is called by the healthz endpoint handler.
|
||||
// It fails (returns an error) if we own the lease but had not been able to renew it.
|
||||
func (l *HealthzAdaptor) Check(req *http.Request) error {
|
||||
l.pointerLock.Lock()
|
||||
defer l.pointerLock.Unlock()
|
||||
if l.le == nil {
|
||||
return nil
|
||||
}
|
||||
return l.le.Check(l.timeout)
|
||||
}
|
||||
|
||||
// SetLeaderElection ties a leader election object to a HealthzAdaptor
|
||||
func (l *HealthzAdaptor) SetLeaderElection(le *LeaderElector) {
|
||||
l.pointerLock.Lock()
|
||||
defer l.pointerLock.Unlock()
|
||||
l.le = le
|
||||
}
|
||||
|
||||
// NewLeaderHealthzAdaptor creates a basic healthz adaptor to monitor a leader election.
|
||||
// timeout determines the time beyond the lease expiry to be allowed for timeout.
|
||||
// checks within the timeout period after the lease expires will still return healthy.
|
||||
func NewLeaderHealthzAdaptor(timeout time.Duration) *HealthzAdaptor {
|
||||
result := &HealthzAdaptor{
|
||||
timeout: timeout,
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package leaderelection implements leader election of a set of endpoints.
|
||||
// It uses an annotation in the endpoints object to store the record of the
|
||||
// election state. This implementation does not guarantee that only one
|
||||
// client is acting as a leader (a.k.a. fencing).
|
||||
//
|
||||
// A client only acts on timestamps captured locally to infer the state of the
|
||||
// leader election. The client does not consider timestamps in the leader
|
||||
// election record to be accurate because these timestamps may not have been
|
||||
// produced by a local clock. The implemention does not depend on their
|
||||
// accuracy and only uses their change to indicate that another client has
|
||||
// renewed the leader lease. Thus the implementation is tolerant to arbitrary
|
||||
// clock skew, but is not tolerant to arbitrary clock skew rate.
|
||||
//
|
||||
// However the level of tolerance to skew rate can be configured by setting
|
||||
// RenewDeadline and LeaseDuration appropriately. The tolerance expressed as a
|
||||
// maximum tolerated ratio of time passed on the fastest node to time passed on
|
||||
// the slowest node can be approximately achieved with a configuration that sets
|
||||
// the same ratio of LeaseDuration to RenewDeadline. For example if a user wanted
|
||||
// to tolerate some nodes progressing forward in time twice as fast as other nodes,
|
||||
// the user could set LeaseDuration to 60 seconds and RenewDeadline to 30 seconds.
|
||||
//
|
||||
// While not required, some method of clock synchronization between nodes in the
|
||||
// cluster is highly recommended. It's important to keep in mind when configuring
|
||||
// this client that the tolerance to skew rate varies inversely to master
|
||||
// availability.
|
||||
//
|
||||
// Larger clusters often have a more lenient SLA for API latency. This should be
|
||||
// taken into account when configuring the client. The rate of leader transitions
|
||||
// should be monitored and RetryPeriod and LeaseDuration should be increased
|
||||
// until the rate is stable and acceptably low. It's important to keep in mind
|
||||
// when configuring this client that the tolerance to API latency varies inversely
|
||||
// to master availability.
|
||||
//
|
||||
// DISCLAIMER: this is an alpha API. This library will likely change significantly
|
||||
// or even be removed entirely in subsequent releases. Depend on this API at
|
||||
// your own risk.
|
||||
package leaderelection
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
rl "k8s.io/client-go/tools/leaderelection/resourcelock"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
const (
|
||||
JitterFactor = 1.2
|
||||
)
|
||||
|
||||
// NewLeaderElector creates a LeaderElector from a LeaderElectionConfig
|
||||
func NewLeaderElector(lec LeaderElectionConfig) (*LeaderElector, error) {
|
||||
if lec.LeaseDuration <= lec.RenewDeadline {
|
||||
return nil, fmt.Errorf("leaseDuration must be greater than renewDeadline")
|
||||
}
|
||||
if lec.RenewDeadline <= time.Duration(JitterFactor*float64(lec.RetryPeriod)) {
|
||||
return nil, fmt.Errorf("renewDeadline must be greater than retryPeriod*JitterFactor")
|
||||
}
|
||||
if lec.LeaseDuration < 1 {
|
||||
return nil, fmt.Errorf("leaseDuration must be greater than zero")
|
||||
}
|
||||
if lec.RenewDeadline < 1 {
|
||||
return nil, fmt.Errorf("renewDeadline must be greater than zero")
|
||||
}
|
||||
if lec.RetryPeriod < 1 {
|
||||
return nil, fmt.Errorf("retryPeriod must be greater than zero")
|
||||
}
|
||||
if lec.Callbacks.OnStartedLeading == nil {
|
||||
return nil, fmt.Errorf("OnStartedLeading callback must not be nil")
|
||||
}
|
||||
if lec.Callbacks.OnStoppedLeading == nil {
|
||||
return nil, fmt.Errorf("OnStoppedLeading callback must not be nil")
|
||||
}
|
||||
|
||||
if lec.Lock == nil {
|
||||
return nil, fmt.Errorf("Lock must not be nil.")
|
||||
}
|
||||
le := LeaderElector{
|
||||
config: lec,
|
||||
clock: clock.RealClock{},
|
||||
metrics: globalMetricsFactory.newLeaderMetrics(),
|
||||
}
|
||||
le.metrics.leaderOff(le.config.Name)
|
||||
return &le, nil
|
||||
}
|
||||
|
||||
type LeaderElectionConfig struct {
|
||||
// Lock is the resource that will be used for locking
|
||||
Lock rl.Interface
|
||||
|
||||
// LeaseDuration is the duration that non-leader candidates will
|
||||
// wait to force acquire leadership. This is measured against time of
|
||||
// last observed ack.
|
||||
//
|
||||
// A client needs to wait a full LeaseDuration without observing a change to
|
||||
// the record before it can attempt to take over. When all clients are
|
||||
// shutdown and a new set of clients are started with different names against
|
||||
// the same leader record, they must wait the full LeaseDuration before
|
||||
// attempting to acquire the lease. Thus LeaseDuration should be as short as
|
||||
// possible (within your tolerance for clock skew rate) to avoid a possible
|
||||
// long waits in the scenario.
|
||||
//
|
||||
// Core clients default this value to 15 seconds.
|
||||
LeaseDuration time.Duration
|
||||
// RenewDeadline is the duration that the acting master will retry
|
||||
// refreshing leadership before giving up.
|
||||
//
|
||||
// Core clients default this value to 10 seconds.
|
||||
RenewDeadline time.Duration
|
||||
// RetryPeriod is the duration the LeaderElector clients should wait
|
||||
// between tries of actions.
|
||||
//
|
||||
// Core clients default this value to 2 seconds.
|
||||
RetryPeriod time.Duration
|
||||
|
||||
// Callbacks are callbacks that are triggered during certain lifecycle
|
||||
// events of the LeaderElector
|
||||
Callbacks LeaderCallbacks
|
||||
|
||||
// WatchDog is the associated health checker
|
||||
// WatchDog may be null if its not needed/configured.
|
||||
WatchDog *HealthzAdaptor
|
||||
|
||||
// ReleaseOnCancel should be set true if the lock should be released
|
||||
// when the run context is cancelled. If you set this to true, you must
|
||||
// ensure all code guarded by this lease has successfully completed
|
||||
// prior to cancelling the context, or you may have two processes
|
||||
// simultaneously acting on the critical path.
|
||||
ReleaseOnCancel bool
|
||||
|
||||
// Name is the name of the resource lock for debugging
|
||||
Name string
|
||||
}
|
||||
|
||||
// LeaderCallbacks are callbacks that are triggered during certain
|
||||
// lifecycle events of the LeaderElector. These are invoked asynchronously.
|
||||
//
|
||||
// possible future callbacks:
|
||||
// * OnChallenge()
|
||||
type LeaderCallbacks struct {
|
||||
// OnStartedLeading is called when a LeaderElector client starts leading
|
||||
OnStartedLeading func(context.Context)
|
||||
// OnStoppedLeading is called when a LeaderElector client stops leading
|
||||
OnStoppedLeading func()
|
||||
// OnNewLeader is called when the client observes a leader that is
|
||||
// not the previously observed leader. This includes the first observed
|
||||
// leader when the client starts.
|
||||
OnNewLeader func(identity string)
|
||||
}
|
||||
|
||||
// LeaderElector is a leader election client.
|
||||
type LeaderElector struct {
|
||||
config LeaderElectionConfig
|
||||
// internal bookkeeping
|
||||
observedRecord rl.LeaderElectionRecord
|
||||
observedRawRecord []byte
|
||||
observedTime time.Time
|
||||
// used to implement OnNewLeader(), may lag slightly from the
|
||||
// value observedRecord.HolderIdentity if the transition has
|
||||
// not yet been reported.
|
||||
reportedLeader string
|
||||
|
||||
// clock is wrapper around time to allow for less flaky testing
|
||||
clock clock.Clock
|
||||
|
||||
metrics leaderMetricsAdapter
|
||||
|
||||
// name is the name of the resource lock for debugging
|
||||
name string
|
||||
}
|
||||
|
||||
// Run starts the leader election loop
|
||||
func (le *LeaderElector) Run(ctx context.Context) {
|
||||
defer func() {
|
||||
runtime.HandleCrash()
|
||||
le.config.Callbacks.OnStoppedLeading()
|
||||
}()
|
||||
if !le.acquire(ctx) {
|
||||
return // ctx signalled done
|
||||
}
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
go le.config.Callbacks.OnStartedLeading(ctx)
|
||||
le.renew(ctx)
|
||||
}
|
||||
|
||||
// RunOrDie starts a client with the provided config or panics if the config
|
||||
// fails to validate.
|
||||
func RunOrDie(ctx context.Context, lec LeaderElectionConfig) {
|
||||
le, err := NewLeaderElector(lec)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if lec.WatchDog != nil {
|
||||
lec.WatchDog.SetLeaderElection(le)
|
||||
}
|
||||
le.Run(ctx)
|
||||
}
|
||||
|
||||
// GetLeader returns the identity of the last observed leader or returns the empty string if
|
||||
// no leader has yet been observed.
|
||||
func (le *LeaderElector) GetLeader() string {
|
||||
return le.observedRecord.HolderIdentity
|
||||
}
|
||||
|
||||
// IsLeader returns true if the last observed leader was this client else returns false.
|
||||
func (le *LeaderElector) IsLeader() bool {
|
||||
return le.observedRecord.HolderIdentity == le.config.Lock.Identity()
|
||||
}
|
||||
|
||||
// acquire loops calling tryAcquireOrRenew and returns true immediately when tryAcquireOrRenew succeeds.
|
||||
// Returns false if ctx signals done.
|
||||
func (le *LeaderElector) acquire(ctx context.Context) bool {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
succeeded := false
|
||||
desc := le.config.Lock.Describe()
|
||||
klog.Infof("attempting to acquire leader lease %v...", desc)
|
||||
wait.JitterUntil(func() {
|
||||
succeeded = le.tryAcquireOrRenew()
|
||||
le.maybeReportTransition()
|
||||
if !succeeded {
|
||||
klog.V(4).Infof("failed to acquire lease %v", desc)
|
||||
return
|
||||
}
|
||||
le.config.Lock.RecordEvent("became leader")
|
||||
le.metrics.leaderOn(le.config.Name)
|
||||
klog.Infof("successfully acquired lease %v", desc)
|
||||
cancel()
|
||||
}, le.config.RetryPeriod, JitterFactor, true, ctx.Done())
|
||||
return succeeded
|
||||
}
|
||||
|
||||
// renew loops calling tryAcquireOrRenew and returns immediately when tryAcquireOrRenew fails or ctx signals done.
|
||||
func (le *LeaderElector) renew(ctx context.Context) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
wait.Until(func() {
|
||||
timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RenewDeadline)
|
||||
defer timeoutCancel()
|
||||
err := wait.PollImmediateUntil(le.config.RetryPeriod, func() (bool, error) {
|
||||
done := make(chan bool, 1)
|
||||
go func() {
|
||||
defer close(done)
|
||||
done <- le.tryAcquireOrRenew()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-timeoutCtx.Done():
|
||||
return false, fmt.Errorf("failed to tryAcquireOrRenew %s", timeoutCtx.Err())
|
||||
case result := <-done:
|
||||
return result, nil
|
||||
}
|
||||
}, timeoutCtx.Done())
|
||||
|
||||
le.maybeReportTransition()
|
||||
desc := le.config.Lock.Describe()
|
||||
if err == nil {
|
||||
klog.V(5).Infof("successfully renewed lease %v", desc)
|
||||
return
|
||||
}
|
||||
le.config.Lock.RecordEvent("stopped leading")
|
||||
le.metrics.leaderOff(le.config.Name)
|
||||
klog.Infof("failed to renew lease %v: %v", desc, err)
|
||||
cancel()
|
||||
}, le.config.RetryPeriod, ctx.Done())
|
||||
|
||||
// if we hold the lease, give it up
|
||||
if le.config.ReleaseOnCancel {
|
||||
le.release()
|
||||
}
|
||||
}
|
||||
|
||||
// release attempts to release the leader lease if we have acquired it.
|
||||
func (le *LeaderElector) release() bool {
|
||||
if !le.IsLeader() {
|
||||
return true
|
||||
}
|
||||
leaderElectionRecord := rl.LeaderElectionRecord{
|
||||
LeaderTransitions: le.observedRecord.LeaderTransitions,
|
||||
}
|
||||
if err := le.config.Lock.Update(leaderElectionRecord); err != nil {
|
||||
klog.Errorf("Failed to release lock: %v", err)
|
||||
return false
|
||||
}
|
||||
le.observedRecord = leaderElectionRecord
|
||||
le.observedTime = le.clock.Now()
|
||||
return true
|
||||
}
|
||||
|
||||
// tryAcquireOrRenew tries to acquire a leader lease if it is not already acquired,
|
||||
// else it tries to renew the lease if it has already been acquired. Returns true
|
||||
// on success else returns false.
|
||||
func (le *LeaderElector) tryAcquireOrRenew() bool {
|
||||
now := metav1.Now()
|
||||
leaderElectionRecord := rl.LeaderElectionRecord{
|
||||
HolderIdentity: le.config.Lock.Identity(),
|
||||
LeaseDurationSeconds: int(le.config.LeaseDuration / time.Second),
|
||||
RenewTime: now,
|
||||
AcquireTime: now,
|
||||
}
|
||||
|
||||
// 1. obtain or create the ElectionRecord
|
||||
oldLeaderElectionRecord, oldLeaderElectionRawRecord, err := le.config.Lock.Get()
|
||||
if err != nil {
|
||||
if !errors.IsNotFound(err) {
|
||||
klog.Errorf("error retrieving resource lock %v: %v", le.config.Lock.Describe(), err)
|
||||
return false
|
||||
}
|
||||
if err = le.config.Lock.Create(leaderElectionRecord); err != nil {
|
||||
klog.Errorf("error initially creating leader election record: %v", err)
|
||||
return false
|
||||
}
|
||||
le.observedRecord = leaderElectionRecord
|
||||
le.observedTime = le.clock.Now()
|
||||
return true
|
||||
}
|
||||
|
||||
// 2. Record obtained, check the Identity & Time
|
||||
if !bytes.Equal(le.observedRawRecord, oldLeaderElectionRawRecord) {
|
||||
le.observedRecord = *oldLeaderElectionRecord
|
||||
le.observedRawRecord = oldLeaderElectionRawRecord
|
||||
le.observedTime = le.clock.Now()
|
||||
}
|
||||
if len(oldLeaderElectionRecord.HolderIdentity) > 0 &&
|
||||
le.observedTime.Add(le.config.LeaseDuration).After(now.Time) &&
|
||||
!le.IsLeader() {
|
||||
klog.V(4).Infof("lock is held by %v and has not yet expired", oldLeaderElectionRecord.HolderIdentity)
|
||||
return false
|
||||
}
|
||||
|
||||
// 3. We're going to try to update. The leaderElectionRecord is set to it's default
|
||||
// here. Let's correct it before updating.
|
||||
if le.IsLeader() {
|
||||
leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime
|
||||
leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions
|
||||
} else {
|
||||
leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1
|
||||
}
|
||||
|
||||
// update the lock itself
|
||||
if err = le.config.Lock.Update(leaderElectionRecord); err != nil {
|
||||
klog.Errorf("Failed to update lock: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
le.observedRecord = leaderElectionRecord
|
||||
le.observedTime = le.clock.Now()
|
||||
return true
|
||||
}
|
||||
|
||||
func (le *LeaderElector) maybeReportTransition() {
|
||||
if le.observedRecord.HolderIdentity == le.reportedLeader {
|
||||
return
|
||||
}
|
||||
le.reportedLeader = le.observedRecord.HolderIdentity
|
||||
if le.config.Callbacks.OnNewLeader != nil {
|
||||
go le.config.Callbacks.OnNewLeader(le.reportedLeader)
|
||||
}
|
||||
}
|
||||
|
||||
// Check will determine if the current lease is expired by more than timeout.
|
||||
func (le *LeaderElector) Check(maxTolerableExpiredLease time.Duration) error {
|
||||
if !le.IsLeader() {
|
||||
// Currently not concerned with the case that we are hot standby
|
||||
return nil
|
||||
}
|
||||
// If we are more than timeout seconds after the lease duration that is past the timeout
|
||||
// on the lease renew. Time to start reporting ourselves as unhealthy. We should have
|
||||
// died but conditions like deadlock can prevent this. (See #70819)
|
||||
if le.clock.Since(le.observedTime) > le.config.LeaseDuration+maxTolerableExpiredLease {
|
||||
return fmt.Errorf("failed election to renew leadership on lease %s", le.config.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package leaderelection
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// This file provides abstractions for setting the provider (e.g., prometheus)
|
||||
// of metrics.
|
||||
|
||||
type leaderMetricsAdapter interface {
|
||||
leaderOn(name string)
|
||||
leaderOff(name string)
|
||||
}
|
||||
|
||||
// GaugeMetric represents a single numerical value that can arbitrarily go up
|
||||
// and down.
|
||||
type SwitchMetric interface {
|
||||
On(name string)
|
||||
Off(name string)
|
||||
}
|
||||
|
||||
type noopMetric struct{}
|
||||
|
||||
func (noopMetric) On(name string) {}
|
||||
func (noopMetric) Off(name string) {}
|
||||
|
||||
// defaultLeaderMetrics expects the caller to lock before setting any metrics.
|
||||
type defaultLeaderMetrics struct {
|
||||
// leader's value indicates if the current process is the owner of name lease
|
||||
leader SwitchMetric
|
||||
}
|
||||
|
||||
func (m *defaultLeaderMetrics) leaderOn(name string) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
m.leader.On(name)
|
||||
}
|
||||
|
||||
func (m *defaultLeaderMetrics) leaderOff(name string) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
m.leader.Off(name)
|
||||
}
|
||||
|
||||
type noMetrics struct{}
|
||||
|
||||
func (noMetrics) leaderOn(name string) {}
|
||||
func (noMetrics) leaderOff(name string) {}
|
||||
|
||||
// MetricsProvider generates various metrics used by the leader election.
|
||||
type MetricsProvider interface {
|
||||
NewLeaderMetric() SwitchMetric
|
||||
}
|
||||
|
||||
type noopMetricsProvider struct{}
|
||||
|
||||
func (_ noopMetricsProvider) NewLeaderMetric() SwitchMetric {
|
||||
return noopMetric{}
|
||||
}
|
||||
|
||||
var globalMetricsFactory = leaderMetricsFactory{
|
||||
metricsProvider: noopMetricsProvider{},
|
||||
}
|
||||
|
||||
type leaderMetricsFactory struct {
|
||||
metricsProvider MetricsProvider
|
||||
|
||||
onlyOnce sync.Once
|
||||
}
|
||||
|
||||
func (f *leaderMetricsFactory) setProvider(mp MetricsProvider) {
|
||||
f.onlyOnce.Do(func() {
|
||||
f.metricsProvider = mp
|
||||
})
|
||||
}
|
||||
|
||||
func (f *leaderMetricsFactory) newLeaderMetrics() leaderMetricsAdapter {
|
||||
mp := f.metricsProvider
|
||||
if mp == (noopMetricsProvider{}) {
|
||||
return noMetrics{}
|
||||
}
|
||||
return &defaultLeaderMetrics{
|
||||
leader: mp.NewLeaderMetric(),
|
||||
}
|
||||
}
|
||||
|
||||
// SetProvider sets the metrics provider for all subsequently created work
|
||||
// queues. Only the first call has an effect.
|
||||
func SetProvider(metricsProvider MetricsProvider) {
|
||||
globalMetricsFactory.setProvider(metricsProvider)
|
||||
}
|
113
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/configmaplock.go
generated
vendored
Normal file
113
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/configmaplock.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resourcelock
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
// TODO: This is almost a exact replica of Endpoints lock.
|
||||
// going forwards as we self host more and more components
|
||||
// and use ConfigMaps as the means to pass that configuration
|
||||
// data we will likely move to deprecate the Endpoints lock.
|
||||
|
||||
type ConfigMapLock struct {
|
||||
// ConfigMapMeta should contain a Name and a Namespace of a
|
||||
// ConfigMapMeta object that the LeaderElector will attempt to lead.
|
||||
ConfigMapMeta metav1.ObjectMeta
|
||||
Client corev1client.ConfigMapsGetter
|
||||
LockConfig ResourceLockConfig
|
||||
cm *v1.ConfigMap
|
||||
}
|
||||
|
||||
// Get returns the election record from a ConfigMap Annotation
|
||||
func (cml *ConfigMapLock) Get() (*LeaderElectionRecord, []byte, error) {
|
||||
var record LeaderElectionRecord
|
||||
var err error
|
||||
cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Get(cml.ConfigMapMeta.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if cml.cm.Annotations == nil {
|
||||
cml.cm.Annotations = make(map[string]string)
|
||||
}
|
||||
recordBytes, found := cml.cm.Annotations[LeaderElectionRecordAnnotationKey]
|
||||
if found {
|
||||
if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return &record, []byte(recordBytes), nil
|
||||
}
|
||||
|
||||
// Create attempts to create a LeaderElectionRecord annotation
|
||||
func (cml *ConfigMapLock) Create(ler LeaderElectionRecord) error {
|
||||
recordBytes, err := json.Marshal(ler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Create(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: cml.ConfigMapMeta.Name,
|
||||
Namespace: cml.ConfigMapMeta.Namespace,
|
||||
Annotations: map[string]string{
|
||||
LeaderElectionRecordAnnotationKey: string(recordBytes),
|
||||
},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Update will update an existing annotation on a given resource.
|
||||
func (cml *ConfigMapLock) Update(ler LeaderElectionRecord) error {
|
||||
if cml.cm == nil {
|
||||
return errors.New("configmap not initialized, call get or create first")
|
||||
}
|
||||
recordBytes, err := json.Marshal(ler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cml.cm.Annotations[LeaderElectionRecordAnnotationKey] = string(recordBytes)
|
||||
cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Update(cml.cm)
|
||||
return err
|
||||
}
|
||||
|
||||
// RecordEvent in leader election while adding meta-data
|
||||
func (cml *ConfigMapLock) RecordEvent(s string) {
|
||||
if cml.LockConfig.EventRecorder == nil {
|
||||
return
|
||||
}
|
||||
events := fmt.Sprintf("%v %v", cml.LockConfig.Identity, s)
|
||||
cml.LockConfig.EventRecorder.Eventf(&v1.ConfigMap{ObjectMeta: cml.cm.ObjectMeta}, v1.EventTypeNormal, "LeaderElection", events)
|
||||
}
|
||||
|
||||
// Describe is used to convert details on current resource lock
|
||||
// into a string
|
||||
func (cml *ConfigMapLock) Describe() string {
|
||||
return fmt.Sprintf("%v/%v", cml.ConfigMapMeta.Namespace, cml.ConfigMapMeta.Name)
|
||||
}
|
||||
|
||||
// Identity returns the Identity of the lock
|
||||
func (cml *ConfigMapLock) Identity() string {
|
||||
return cml.LockConfig.Identity
|
||||
}
|
111
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/endpointslock.go
generated
vendored
Normal file
111
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/endpointslock.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resourcelock
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
type EndpointsLock struct {
|
||||
// EndpointsMeta should contain a Name and a Namespace of an
|
||||
// Endpoints object that the LeaderElector will attempt to lead.
|
||||
EndpointsMeta metav1.ObjectMeta
|
||||
Client corev1client.EndpointsGetter
|
||||
LockConfig ResourceLockConfig
|
||||
e *v1.Endpoints
|
||||
}
|
||||
|
||||
// Get returns the election record from a Endpoints Annotation
|
||||
func (el *EndpointsLock) Get() (*LeaderElectionRecord, []byte, error) {
|
||||
var record LeaderElectionRecord
|
||||
var err error
|
||||
el.e, err = el.Client.Endpoints(el.EndpointsMeta.Namespace).Get(el.EndpointsMeta.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if el.e.Annotations == nil {
|
||||
el.e.Annotations = make(map[string]string)
|
||||
}
|
||||
recordBytes, found := el.e.Annotations[LeaderElectionRecordAnnotationKey]
|
||||
if found {
|
||||
if err := json.Unmarshal([]byte(recordBytes), &record); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return &record, []byte(recordBytes), nil
|
||||
}
|
||||
|
||||
// Create attempts to create a LeaderElectionRecord annotation
|
||||
func (el *EndpointsLock) Create(ler LeaderElectionRecord) error {
|
||||
recordBytes, err := json.Marshal(ler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
el.e, err = el.Client.Endpoints(el.EndpointsMeta.Namespace).Create(&v1.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: el.EndpointsMeta.Name,
|
||||
Namespace: el.EndpointsMeta.Namespace,
|
||||
Annotations: map[string]string{
|
||||
LeaderElectionRecordAnnotationKey: string(recordBytes),
|
||||
},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Update will update and existing annotation on a given resource.
|
||||
func (el *EndpointsLock) Update(ler LeaderElectionRecord) error {
|
||||
if el.e == nil {
|
||||
return errors.New("endpoint not initialized, call get or create first")
|
||||
}
|
||||
recordBytes, err := json.Marshal(ler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if el.e.Annotations == nil {
|
||||
el.e.Annotations = make(map[string]string)
|
||||
}
|
||||
el.e.Annotations[LeaderElectionRecordAnnotationKey] = string(recordBytes)
|
||||
el.e, err = el.Client.Endpoints(el.EndpointsMeta.Namespace).Update(el.e)
|
||||
return err
|
||||
}
|
||||
|
||||
// RecordEvent in leader election while adding meta-data
|
||||
func (el *EndpointsLock) RecordEvent(s string) {
|
||||
if el.LockConfig.EventRecorder == nil {
|
||||
return
|
||||
}
|
||||
events := fmt.Sprintf("%v %v", el.LockConfig.Identity, s)
|
||||
el.LockConfig.EventRecorder.Eventf(&v1.Endpoints{ObjectMeta: el.e.ObjectMeta}, v1.EventTypeNormal, "LeaderElection", events)
|
||||
}
|
||||
|
||||
// Describe is used to convert details on current resource lock
|
||||
// into a string
|
||||
func (el *EndpointsLock) Describe() string {
|
||||
return fmt.Sprintf("%v/%v", el.EndpointsMeta.Namespace, el.EndpointsMeta.Name)
|
||||
}
|
||||
|
||||
// Identity returns the Identity of the lock
|
||||
func (el *EndpointsLock) Identity() string {
|
||||
return el.LockConfig.Identity
|
||||
}
|
141
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/interface.go
generated
vendored
Normal file
141
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/interface.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resourcelock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
coordinationv1 "k8s.io/client-go/kubernetes/typed/coordination/v1"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
LeaderElectionRecordAnnotationKey = "control-plane.alpha.kubernetes.io/leader"
|
||||
EndpointsResourceLock = "endpoints"
|
||||
ConfigMapsResourceLock = "configmaps"
|
||||
LeasesResourceLock = "leases"
|
||||
EndpointsLeasesResourceLock = "endpointsleases"
|
||||
ConfigMapsLeasesResourceLock = "configmapsleases"
|
||||
)
|
||||
|
||||
// LeaderElectionRecord is the record that is stored in the leader election annotation.
|
||||
// This information should be used for observational purposes only and could be replaced
|
||||
// with a random string (e.g. UUID) with only slight modification of this code.
|
||||
// TODO(mikedanese): this should potentially be versioned
|
||||
type LeaderElectionRecord struct {
|
||||
// HolderIdentity is the ID that owns the lease. If empty, no one owns this lease and
|
||||
// all callers may acquire. Versions of this library prior to Kubernetes 1.14 will not
|
||||
// attempt to acquire leases with empty identities and will wait for the full lease
|
||||
// interval to expire before attempting to reacquire. This value is set to empty when
|
||||
// a client voluntarily steps down.
|
||||
HolderIdentity string `json:"holderIdentity"`
|
||||
LeaseDurationSeconds int `json:"leaseDurationSeconds"`
|
||||
AcquireTime metav1.Time `json:"acquireTime"`
|
||||
RenewTime metav1.Time `json:"renewTime"`
|
||||
LeaderTransitions int `json:"leaderTransitions"`
|
||||
}
|
||||
|
||||
// EventRecorder records a change in the ResourceLock.
|
||||
type EventRecorder interface {
|
||||
Eventf(obj runtime.Object, eventType, reason, message string, args ...interface{})
|
||||
}
|
||||
|
||||
// ResourceLockConfig common data that exists across different
|
||||
// resource locks
|
||||
type ResourceLockConfig struct {
|
||||
// Identity is the unique string identifying a lease holder across
|
||||
// all participants in an election.
|
||||
Identity string
|
||||
// EventRecorder is optional.
|
||||
EventRecorder EventRecorder
|
||||
}
|
||||
|
||||
// Interface offers a common interface for locking on arbitrary
|
||||
// resources used in leader election. The Interface is used
|
||||
// to hide the details on specific implementations in order to allow
|
||||
// them to change over time. This interface is strictly for use
|
||||
// by the leaderelection code.
|
||||
type Interface interface {
|
||||
// Get returns the LeaderElectionRecord
|
||||
Get() (*LeaderElectionRecord, []byte, error)
|
||||
|
||||
// Create attempts to create a LeaderElectionRecord
|
||||
Create(ler LeaderElectionRecord) error
|
||||
|
||||
// Update will update and existing LeaderElectionRecord
|
||||
Update(ler LeaderElectionRecord) error
|
||||
|
||||
// RecordEvent is used to record events
|
||||
RecordEvent(string)
|
||||
|
||||
// Identity will return the locks Identity
|
||||
Identity() string
|
||||
|
||||
// Describe is used to convert details on current resource lock
|
||||
// into a string
|
||||
Describe() string
|
||||
}
|
||||
|
||||
// Manufacture will create a lock of a given type according to the input parameters
|
||||
func New(lockType string, ns string, name string, coreClient corev1.CoreV1Interface, coordinationClient coordinationv1.CoordinationV1Interface, rlc ResourceLockConfig) (Interface, error) {
|
||||
endpointsLock := &EndpointsLock{
|
||||
EndpointsMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
Client: coreClient,
|
||||
LockConfig: rlc,
|
||||
}
|
||||
configmapLock := &ConfigMapLock{
|
||||
ConfigMapMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
Client: coreClient,
|
||||
LockConfig: rlc,
|
||||
}
|
||||
leaseLock := &LeaseLock{
|
||||
LeaseMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
Client: coordinationClient,
|
||||
LockConfig: rlc,
|
||||
}
|
||||
switch lockType {
|
||||
case EndpointsResourceLock:
|
||||
return endpointsLock, nil
|
||||
case ConfigMapsResourceLock:
|
||||
return configmapLock, nil
|
||||
case LeasesResourceLock:
|
||||
return leaseLock, nil
|
||||
case EndpointsLeasesResourceLock:
|
||||
return &MultiLock{
|
||||
Primary: endpointsLock,
|
||||
Secondary: leaseLock,
|
||||
}, nil
|
||||
case ConfigMapsLeasesResourceLock:
|
||||
return &MultiLock{
|
||||
Primary: configmapLock,
|
||||
Secondary: leaseLock,
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid lock-type %s", lockType)
|
||||
}
|
||||
}
|
129
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go
generated
vendored
Normal file
129
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/leaselock.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resourcelock
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
coordinationv1 "k8s.io/api/coordination/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
coordinationv1client "k8s.io/client-go/kubernetes/typed/coordination/v1"
|
||||
)
|
||||
|
||||
type LeaseLock struct {
|
||||
// LeaseMeta should contain a Name and a Namespace of a
|
||||
// LeaseMeta object that the LeaderElector will attempt to lead.
|
||||
LeaseMeta metav1.ObjectMeta
|
||||
Client coordinationv1client.LeasesGetter
|
||||
LockConfig ResourceLockConfig
|
||||
lease *coordinationv1.Lease
|
||||
}
|
||||
|
||||
// Get returns the election record from a Lease spec
|
||||
func (ll *LeaseLock) Get() (*LeaderElectionRecord, []byte, error) {
|
||||
var err error
|
||||
ll.lease, err = ll.Client.Leases(ll.LeaseMeta.Namespace).Get(ll.LeaseMeta.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
record := LeaseSpecToLeaderElectionRecord(&ll.lease.Spec)
|
||||
recordByte, err := json.Marshal(*record)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return record, recordByte, nil
|
||||
}
|
||||
|
||||
// Create attempts to create a Lease
|
||||
func (ll *LeaseLock) Create(ler LeaderElectionRecord) error {
|
||||
var err error
|
||||
ll.lease, err = ll.Client.Leases(ll.LeaseMeta.Namespace).Create(&coordinationv1.Lease{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: ll.LeaseMeta.Name,
|
||||
Namespace: ll.LeaseMeta.Namespace,
|
||||
},
|
||||
Spec: LeaderElectionRecordToLeaseSpec(&ler),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Update will update an existing Lease spec.
|
||||
func (ll *LeaseLock) Update(ler LeaderElectionRecord) error {
|
||||
if ll.lease == nil {
|
||||
return errors.New("lease not initialized, call get or create first")
|
||||
}
|
||||
ll.lease.Spec = LeaderElectionRecordToLeaseSpec(&ler)
|
||||
var err error
|
||||
ll.lease, err = ll.Client.Leases(ll.LeaseMeta.Namespace).Update(ll.lease)
|
||||
return err
|
||||
}
|
||||
|
||||
// RecordEvent in leader election while adding meta-data
|
||||
func (ll *LeaseLock) RecordEvent(s string) {
|
||||
if ll.LockConfig.EventRecorder == nil {
|
||||
return
|
||||
}
|
||||
events := fmt.Sprintf("%v %v", ll.LockConfig.Identity, s)
|
||||
ll.LockConfig.EventRecorder.Eventf(&coordinationv1.Lease{ObjectMeta: ll.lease.ObjectMeta}, corev1.EventTypeNormal, "LeaderElection", events)
|
||||
}
|
||||
|
||||
// Describe is used to convert details on current resource lock
|
||||
// into a string
|
||||
func (ll *LeaseLock) Describe() string {
|
||||
return fmt.Sprintf("%v/%v", ll.LeaseMeta.Namespace, ll.LeaseMeta.Name)
|
||||
}
|
||||
|
||||
// Identity returns the Identity of the lock
|
||||
func (ll *LeaseLock) Identity() string {
|
||||
return ll.LockConfig.Identity
|
||||
}
|
||||
|
||||
func LeaseSpecToLeaderElectionRecord(spec *coordinationv1.LeaseSpec) *LeaderElectionRecord {
|
||||
var r LeaderElectionRecord
|
||||
if spec.HolderIdentity != nil {
|
||||
r.HolderIdentity = *spec.HolderIdentity
|
||||
}
|
||||
if spec.LeaseDurationSeconds != nil {
|
||||
r.LeaseDurationSeconds = int(*spec.LeaseDurationSeconds)
|
||||
}
|
||||
if spec.LeaseTransitions != nil {
|
||||
r.LeaderTransitions = int(*spec.LeaseTransitions)
|
||||
}
|
||||
if spec.AcquireTime != nil {
|
||||
r.AcquireTime = metav1.Time{spec.AcquireTime.Time}
|
||||
}
|
||||
if spec.RenewTime != nil {
|
||||
r.RenewTime = metav1.Time{spec.RenewTime.Time}
|
||||
}
|
||||
return &r
|
||||
|
||||
}
|
||||
|
||||
func LeaderElectionRecordToLeaseSpec(ler *LeaderElectionRecord) coordinationv1.LeaseSpec {
|
||||
leaseDurationSeconds := int32(ler.LeaseDurationSeconds)
|
||||
leaseTransitions := int32(ler.LeaderTransitions)
|
||||
return coordinationv1.LeaseSpec{
|
||||
HolderIdentity: &ler.HolderIdentity,
|
||||
LeaseDurationSeconds: &leaseDurationSeconds,
|
||||
AcquireTime: &metav1.MicroTime{ler.AcquireTime.Time},
|
||||
RenewTime: &metav1.MicroTime{ler.RenewTime.Time},
|
||||
LeaseTransitions: &leaseTransitions,
|
||||
}
|
||||
}
|
103
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/multilock.go
generated
vendored
Normal file
103
vendor/k8s.io/client-go/tools/leaderelection/resourcelock/multilock.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resourcelock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
UnknownLeader = "leaderelection.k8s.io/unknown"
|
||||
)
|
||||
|
||||
// MultiLock is used for lock's migration
|
||||
type MultiLock struct {
|
||||
Primary Interface
|
||||
Secondary Interface
|
||||
}
|
||||
|
||||
// Get returns the older election record of the lock
|
||||
func (ml *MultiLock) Get() (*LeaderElectionRecord, []byte, error) {
|
||||
primary, primaryRaw, err := ml.Primary.Get()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
secondary, secondaryRaw, err := ml.Secondary.Get()
|
||||
if err != nil {
|
||||
// Lock is held by old client
|
||||
if apierrors.IsNotFound(err) && primary.HolderIdentity != ml.Identity() {
|
||||
return primary, primaryRaw, nil
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if primary.HolderIdentity != secondary.HolderIdentity {
|
||||
primary.HolderIdentity = UnknownLeader
|
||||
primaryRaw, err = json.Marshal(primary)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return primary, ConcatRawRecord(primaryRaw, secondaryRaw), nil
|
||||
}
|
||||
|
||||
// Create attempts to create both primary lock and secondary lock
|
||||
func (ml *MultiLock) Create(ler LeaderElectionRecord) error {
|
||||
err := ml.Primary.Create(ler)
|
||||
if err != nil && !apierrors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
return ml.Secondary.Create(ler)
|
||||
}
|
||||
|
||||
// Update will update and existing annotation on both two resources.
|
||||
func (ml *MultiLock) Update(ler LeaderElectionRecord) error {
|
||||
err := ml.Primary.Update(ler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, err = ml.Secondary.Get()
|
||||
if err != nil && apierrors.IsNotFound(err) {
|
||||
return ml.Secondary.Create(ler)
|
||||
}
|
||||
return ml.Secondary.Update(ler)
|
||||
}
|
||||
|
||||
// RecordEvent in leader election while adding meta-data
|
||||
func (ml *MultiLock) RecordEvent(s string) {
|
||||
ml.Primary.RecordEvent(s)
|
||||
ml.Secondary.RecordEvent(s)
|
||||
}
|
||||
|
||||
// Describe is used to convert details on current resource lock
|
||||
// into a string
|
||||
func (ml *MultiLock) Describe() string {
|
||||
return ml.Primary.Describe()
|
||||
}
|
||||
|
||||
// Identity returns the Identity of the lock
|
||||
func (ml *MultiLock) Identity() string {
|
||||
return ml.Primary.Identity()
|
||||
}
|
||||
|
||||
func ConcatRawRecord(primaryRaw, secondaryRaw []byte) []byte {
|
||||
return bytes.Join([][]byte{primaryRaw, secondaryRaw}, []byte(","))
|
||||
}
|
|
@ -184,25 +184,25 @@ func (r conditionsImpl) GetCondition(t ConditionType) *Condition {
|
|||
|
||||
// SetCondition sets or updates the Condition on Conditions for Condition.Type.
|
||||
// If there is an update, Conditions are stored back sorted.
|
||||
func (r conditionsImpl) SetCondition(new Condition) {
|
||||
func (r conditionsImpl) SetCondition(cond Condition) {
|
||||
if r.accessor == nil {
|
||||
return
|
||||
}
|
||||
t := new.Type
|
||||
t := cond.Type
|
||||
var conditions Conditions
|
||||
for _, c := range r.accessor.GetConditions() {
|
||||
if c.Type != t {
|
||||
conditions = append(conditions, c)
|
||||
} else {
|
||||
// If we'd only update the LastTransitionTime, then return.
|
||||
new.LastTransitionTime = c.LastTransitionTime
|
||||
if reflect.DeepEqual(&new, &c) {
|
||||
cond.LastTransitionTime = c.LastTransitionTime
|
||||
if reflect.DeepEqual(cond, c) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
new.LastTransitionTime = VolatileTime{Inner: metav1.NewTime(time.Now())}
|
||||
conditions = append(conditions, new)
|
||||
cond.LastTransitionTime = VolatileTime{Inner: metav1.NewTime(time.Now())}
|
||||
conditions = append(conditions, cond)
|
||||
// Sorted for convenience of the consumer, i.e. kubectl.
|
||||
sort.Slice(conditions, func(i, j int) bool { return conditions[i].Type < conditions[j].Type })
|
||||
r.accessor.SetConditions(conditions)
|
||||
|
|
|
@ -36,7 +36,7 @@ func CheckDeprecated(ctx context.Context, obj interface{}) *FieldError {
|
|||
// CheckDeprecated checks whether the provided named deprecated fields
|
||||
// are set in a context where deprecation is disallowed.
|
||||
// This is a json shallow check. We will recursively check inlined structs.
|
||||
func CheckDeprecatedUpdate(ctx context.Context, obj interface{}, original interface{}) *FieldError {
|
||||
func CheckDeprecatedUpdate(ctx context.Context, obj, original interface{}) *FieldError {
|
||||
if IsDeprecatedAllowed(ctx) {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,4 +18,12 @@ package duck
|
|||
|
||||
const (
|
||||
GroupName = "duck.knative.dev"
|
||||
|
||||
// AddressableDuckVersionLabel is the label we use to declare
|
||||
// that a type conforms to the Addressable duck type.
|
||||
AddressableDuckVersionLabel = "duck.knative.dev/addressable"
|
||||
|
||||
// SourceDuckVersionLabel is the label we use to declare
|
||||
// that a type conforms to the Source duck type.
|
||||
SourceDuckVersionLabel = "duck.knative.dev/source"
|
||||
)
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"knative.dev/pkg/apis"
|
||||
"knative.dev/pkg/apis/duck"
|
||||
"knative.dev/pkg/kmeta"
|
||||
)
|
||||
|
||||
// +genduck
|
||||
|
@ -45,6 +46,12 @@ type Status struct {
|
|||
// +patchMergeKey=type
|
||||
// +patchStrategy=merge
|
||||
Conditions Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
|
||||
|
||||
// Annotations is additional Status fields for the Resource to save some
|
||||
// additional State as well as convey more information to the user. This is
|
||||
// roughly akin to Annotations on any k8s resource, just the reconciler conveying
|
||||
// richer information outwards.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
var _ apis.ConditionsAccessor = (*Status)(nil)
|
||||
|
@ -87,6 +94,10 @@ func (s *Status) GetCondition(t apis.ConditionType) *apis.Condition {
|
|||
// return true the condition type will be copied to the sink
|
||||
func (source *Status) ConvertTo(ctx context.Context, sink *Status, predicates ...func(apis.ConditionType) bool) {
|
||||
sink.ObservedGeneration = source.ObservedGeneration
|
||||
if source.Annotations != nil {
|
||||
// This will deep copy the map.
|
||||
sink.Annotations = kmeta.UnionMaps(source.Annotations)
|
||||
}
|
||||
|
||||
conditions := make(apis.Conditions, 0, len(source.Conditions))
|
||||
for _, c := range source.Conditions {
|
||||
|
|
|
@ -428,6 +428,13 @@ func (in *Status) DeepCopyInto(out *Status) {
|
|||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -204,11 +204,12 @@ func flatten(path []string) string {
|
|||
var newPath []string
|
||||
for _, part := range path {
|
||||
for _, p := range strings.Split(part, ".") {
|
||||
if p == CurrentField {
|
||||
switch {
|
||||
case p == CurrentField:
|
||||
continue
|
||||
} else if len(newPath) > 0 && isIndex(p) {
|
||||
case len(newPath) > 0 && isIndex(p):
|
||||
newPath[len(newPath)-1] += p
|
||||
} else {
|
||||
default:
|
||||
newPath = append(newPath, p)
|
||||
}
|
||||
}
|
||||
|
@ -383,7 +384,7 @@ func ErrOutOfBoundsValue(value, lower, upper interface{}, fieldPath string) *Fie
|
|||
func CheckDisallowedFields(request, maskedRequest interface{}) *FieldError {
|
||||
if disallowed, err := kmp.CompareSetFields(request, maskedRequest); err != nil {
|
||||
return &FieldError{
|
||||
Message: fmt.Sprintf("Internal Error"),
|
||||
Message: "Internal Error",
|
||||
Paths: []string{CurrentField},
|
||||
}
|
||||
} else if len(disallowed) > 0 {
|
||||
|
|
|
@ -201,7 +201,7 @@ func isKRShaped(tags map[string]map[string]string) bool {
|
|||
if !has {
|
||||
return false
|
||||
}
|
||||
return vals["krshapedlogic"] == "true"
|
||||
return vals["krshapedlogic"] != "false"
|
||||
}
|
||||
|
||||
func isNonNamespaced(tags map[string]map[string]string) bool {
|
||||
|
|
|
@ -142,9 +142,21 @@ func (g *reconcilerControllerGenerator) GenerateType(c *generator.Context, t *ty
|
|||
Package: "context",
|
||||
Name: "Context",
|
||||
}),
|
||||
"fmtSprintf": c.Universe.Function(types.Name{
|
||||
Package: "fmt",
|
||||
Name: "Sprintf",
|
||||
"reconcilerLeaderAwareFuncs": c.Universe.Type(types.Name{
|
||||
Package: "knative.dev/pkg/reconciler",
|
||||
Name: "LeaderAwareFuncs",
|
||||
}),
|
||||
"reconcilerBucket": c.Universe.Type(types.Name{
|
||||
Package: "knative.dev/pkg/reconciler",
|
||||
Name: "Bucket",
|
||||
}),
|
||||
"typesNamespacedName": c.Universe.Type(types.Name{
|
||||
Package: "k8s.io/apimachinery/pkg/types",
|
||||
Name: "NamespacedName",
|
||||
}),
|
||||
"labelsEverything": c.Universe.Function(types.Name{
|
||||
Package: "k8s.io/apimachinery/pkg/labels",
|
||||
Name: "Everything",
|
||||
}),
|
||||
"stringsReplaceAll": c.Universe.Function(types.Name{
|
||||
Package: "strings",
|
||||
|
@ -154,6 +166,10 @@ func (g *reconcilerControllerGenerator) GenerateType(c *generator.Context, t *ty
|
|||
Package: "reflect",
|
||||
Name: "TypeOf",
|
||||
}),
|
||||
"fmtSprintf": c.Universe.Function(types.Name{
|
||||
Package: "fmt",
|
||||
Name: "Sprintf",
|
||||
}),
|
||||
}
|
||||
|
||||
sw.Do(reconcilerControllerNewImpl, m)
|
||||
|
@ -185,9 +201,27 @@ func NewImpl(ctx {{.contextContext|raw}}, r Interface{{if .hasClass}}, classValu
|
|||
|
||||
{{.type|lowercaseSingular}}Informer := {{.informerGet|raw}}(ctx)
|
||||
|
||||
lister := {{.type|lowercaseSingular}}Informer.Lister()
|
||||
|
||||
rec := &reconcilerImpl{
|
||||
LeaderAwareFuncs: {{.reconcilerLeaderAwareFuncs|raw}}{
|
||||
PromoteFunc: func(bkt {{.reconcilerBucket|raw}}, enq func({{.reconcilerBucket|raw}}, {{.typesNamespacedName|raw}})) error {
|
||||
all, err := lister.List({{.labelsEverything|raw}}())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, elt := range all {
|
||||
// TODO: Consider letting users specify a filter in options.
|
||||
enq(bkt, {{.typesNamespacedName|raw}}{
|
||||
Namespace: elt.GetNamespace(),
|
||||
Name: elt.GetName(),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
Client: {{.clientGet|raw}}(ctx),
|
||||
Lister: {{.type|lowercaseSingular}}Informer.Lister(),
|
||||
Lister: lister,
|
||||
reconciler: r,
|
||||
finalizerName: defaultFinalizerName,
|
||||
{{if .hasClass}}classValue: classValue,{{end}}
|
||||
|
@ -211,6 +245,9 @@ func NewImpl(ctx {{.contextContext|raw}}, r Interface{{if .hasClass}}, classValu
|
|||
if opts.AgentName != "" {
|
||||
agentName = opts.AgentName
|
||||
}
|
||||
if opts.SkipStatusUpdates {
|
||||
rec.skipStatusUpdates = true
|
||||
}
|
||||
}
|
||||
|
||||
rec.Recorder = createRecorder(ctx, agentName)
|
||||
|
|
|
@ -147,10 +147,35 @@ func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *ty
|
|||
Package: "context",
|
||||
Name: "Context",
|
||||
}),
|
||||
"fmtErrorf": c.Universe.Package("fmt").Function("Errorf"),
|
||||
"reflectDeepEqual": c.Universe.Package("reflect").Function("DeepEqual"),
|
||||
"equalitySemantic": c.Universe.Package("k8s.io/apimachinery/pkg/api/equality").Variable("Semantic"),
|
||||
"jsonMarshal": c.Universe.Package("encoding/json").Function("Marshal"),
|
||||
"typesMergePatchType": c.Universe.Package("k8s.io/apimachinery/pkg/types").Constant("MergePatchType"),
|
||||
"syncRWMutex": c.Universe.Type(types.Name{
|
||||
Package: "sync",
|
||||
Name: "RWMutex",
|
||||
}),
|
||||
"reconcilerLeaderAware": c.Universe.Type(types.Name{
|
||||
Package: "knative.dev/pkg/reconciler",
|
||||
Name: "LeaderAware",
|
||||
}),
|
||||
"reconcilerLeaderAwareFuncs": c.Universe.Type(types.Name{
|
||||
Package: "knative.dev/pkg/reconciler",
|
||||
Name: "LeaderAwareFuncs",
|
||||
}),
|
||||
"reconcilerBucket": c.Universe.Type(types.Name{
|
||||
Package: "knative.dev/pkg/reconciler",
|
||||
Name: "Bucket",
|
||||
}),
|
||||
"typesNamespacedName": c.Universe.Type(types.Name{
|
||||
Package: "k8s.io/apimachinery/pkg/types",
|
||||
Name: "NamespacedName",
|
||||
}),
|
||||
"labelsEverything": c.Universe.Function(types.Name{
|
||||
Package: "k8s.io/apimachinery/pkg/labels",
|
||||
Name: "Everything",
|
||||
}),
|
||||
}
|
||||
|
||||
sw.Do(reconcilerInterfaceFactory, m)
|
||||
|
@ -186,8 +211,30 @@ type Finalizer interface {
|
|||
FinalizeKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
|
||||
}
|
||||
|
||||
// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a
|
||||
// controller reconciling {{.type|raw}} if they want to process resources for which
|
||||
// they are not the leader.
|
||||
type ReadOnlyInterface interface {
|
||||
// ObserveKind implements logic to observe {{.type|raw}}.
|
||||
// This method should not write to the API.
|
||||
ObserveKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
|
||||
}
|
||||
|
||||
// ReadOnlyFinalizer defines the strongly typed interfaces to be implemented by a
|
||||
// controller finalizing {{.type|raw}} if they want to process tombstoned resources
|
||||
// even when they are not the leader. Due to the nature of how finalizers are handled
|
||||
// there are no guarantees that this will be called.
|
||||
type ReadOnlyFinalizer interface {
|
||||
// ObserveFinalizeKind implements custom logic to observe the final state of {{.type|raw}}.
|
||||
// This method should not write to the API.
|
||||
ObserveFinalizeKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
|
||||
}
|
||||
|
||||
// reconcilerImpl implements controller.Reconciler for {{.type|raw}} resources.
|
||||
type reconcilerImpl struct {
|
||||
// LeaderAwareFuncs is inlined to help us implement {{.reconcilerLeaderAware|raw}}
|
||||
{{.reconcilerLeaderAwareFuncs|raw}}
|
||||
|
||||
// Client is used to write back status updates.
|
||||
Client {{.clientsetInterface|raw}}
|
||||
|
||||
|
@ -208,6 +255,10 @@ type reconcilerImpl struct {
|
|||
// finalizerName is the name of the finalizer to reconcile.
|
||||
finalizerName string
|
||||
|
||||
// skipStatusUpdates configures whether or not this reconciler automatically updates
|
||||
// the status of the reconciled resource.
|
||||
skipStatusUpdates bool
|
||||
|
||||
{{if .hasClass}}
|
||||
// classValue is the resource annotation[{{ .class }}] instance value this reconciler instance filters on.
|
||||
classValue string
|
||||
|
@ -216,6 +267,8 @@ type reconcilerImpl struct {
|
|||
|
||||
// Check that our Reconciler implements controller.Reconciler
|
||||
var _ controller.Reconciler = (*reconcilerImpl)(nil)
|
||||
// Check that our generated Reconciler is always LeaderAware.
|
||||
var _ {{.reconcilerLeaderAware|raw}} = (*reconcilerImpl)(nil)
|
||||
|
||||
`
|
||||
|
||||
|
@ -226,7 +279,30 @@ func NewReconciler(ctx {{.contextContext|raw}}, logger *{{.zapSugaredLogger|raw}
|
|||
logger.Fatalf("up to one options struct is supported, found %d", len(options))
|
||||
}
|
||||
|
||||
// Fail fast when users inadvertently implement the other LeaderAware interface.
|
||||
// For the typed reconcilers, Promote shouldn't take any arguments.
|
||||
if _, ok := r.({{.reconcilerLeaderAware|raw}}); ok {
|
||||
logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r)
|
||||
}
|
||||
// TODO: Consider validating when folks implement ReadOnlyFinalizer, but not Finalizer.
|
||||
|
||||
rec := &reconcilerImpl{
|
||||
LeaderAwareFuncs: {{.reconcilerLeaderAwareFuncs|raw}}{
|
||||
PromoteFunc: func(bkt {{.reconcilerBucket|raw}}, enq func({{.reconcilerBucket|raw}}, {{.typesNamespacedName|raw}})) error {
|
||||
all, err := lister.List({{.labelsEverything|raw}}())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, elt := range all {
|
||||
// TODO: Consider letting users specify a filter in options.
|
||||
enq(bkt, {{.typesNamespacedName|raw}}{
|
||||
Namespace: elt.GetNamespace(),
|
||||
Name: elt.GetName(),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
Client: client,
|
||||
Lister: lister,
|
||||
Recorder: recorder,
|
||||
|
@ -253,6 +329,25 @@ var reconcilerImplFactory = `
|
|||
func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) error {
|
||||
logger := {{.loggingFromContext|raw}}(ctx)
|
||||
|
||||
// Convert the namespace/name string into a distinct namespace and name
|
||||
namespace, name, err := {{.cacheSplitMetaNamespaceKey|raw}}(key)
|
||||
if err != nil {
|
||||
logger.Errorf("invalid resource key: %s", key)
|
||||
return nil
|
||||
}
|
||||
// Establish whether we are the leader for use below.
|
||||
isLeader := r.IsLeaderFor({{.typesNamespacedName|raw}}{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
})
|
||||
roi, isROI := r.reconciler.(ReadOnlyInterface)
|
||||
rof, isROF := r.reconciler.(ReadOnlyFinalizer);
|
||||
if !isLeader && !isROI && !isROF {
|
||||
// If we are not the leader, and we don't implement either ReadOnly
|
||||
// interface, then take a fast-path out.
|
||||
return nil
|
||||
}
|
||||
|
||||
// If configStore is set, attach the frozen configuration to the context.
|
||||
if r.configStore != nil {
|
||||
ctx = r.configStore.ToContext(ctx)
|
||||
|
@ -261,19 +356,7 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
|
|||
// Add the recorder to context.
|
||||
ctx = {{.controllerWithEventRecorder|raw}}(ctx, r.Recorder)
|
||||
|
||||
// Convert the namespace/name string into a distinct namespace and name
|
||||
{{if .nonNamespaced}}
|
||||
_, name, err := {{.cacheSplitMetaNamespaceKey|raw}}(key)
|
||||
{{else}}
|
||||
namespace, name, err := {{.cacheSplitMetaNamespaceKey|raw}}(key)
|
||||
{{end}}
|
||||
if err != nil {
|
||||
logger.Errorf("invalid resource key: %s", key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the resource with this namespace/name.
|
||||
|
||||
{{if .nonNamespaced}}
|
||||
getter := r.Lister
|
||||
{{else}}
|
||||
|
@ -302,27 +385,34 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
|
|||
|
||||
var reconcileEvent {{.reconcilerEvent|raw}}
|
||||
if resource.GetDeletionTimestamp().IsZero() {
|
||||
// Append the target method to the logger.
|
||||
logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
|
||||
if isLeader {
|
||||
// Append the target method to the logger.
|
||||
logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
|
||||
|
||||
// Set and update the finalizer on resource if r.reconciler
|
||||
// implements Finalizer.
|
||||
if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
|
||||
logger.Warnw("Failed to set finalizers", zap.Error(err))
|
||||
// Set and update the finalizer on resource if r.reconciler
|
||||
// implements Finalizer.
|
||||
if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
|
||||
return {{.fmtErrorf|raw}}("failed to set finalizers: %w", err)
|
||||
}
|
||||
{{if .isKRShaped}}
|
||||
reconciler.PreProcessReconcile(ctx, resource)
|
||||
{{end}}
|
||||
|
||||
// Reconcile this copy of the resource and then write back any status
|
||||
// updates regardless of whether the reconciliation errored out.
|
||||
reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
|
||||
|
||||
{{if .isKRShaped}}
|
||||
reconciler.PostProcessReconcile(ctx, resource, original)
|
||||
{{end}}
|
||||
} else if isROI {
|
||||
// Append the target method to the logger.
|
||||
logger = logger.With(zap.String("targetMethod", "ObserveKind"))
|
||||
|
||||
// Observe any changes to this resource, since we are not the leader.
|
||||
reconcileEvent = roi.ObserveKind(ctx, resource)
|
||||
}
|
||||
|
||||
{{if .isKRShaped}}
|
||||
reconciler.PreProcessReconcile(ctx, resource)
|
||||
{{end}}
|
||||
|
||||
// Reconcile this copy of the resource and then write back any status
|
||||
// updates regardless of whether the reconciliation errored out.
|
||||
reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
|
||||
|
||||
{{if .isKRShaped}}
|
||||
reconciler.PostProcessReconcile(ctx, resource)
|
||||
{{end}}
|
||||
} else if fin, ok := r.reconciler.(Finalizer); ok {
|
||||
} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
|
||||
// Append the target method to the logger.
|
||||
logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
|
||||
|
||||
|
@ -330,21 +420,32 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
|
|||
// and reconciled cleanly (nil or normal event), remove the finalizer.
|
||||
reconcileEvent = fin.FinalizeKind(ctx, resource)
|
||||
if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
|
||||
logger.Warnw("Failed to clear finalizers", zap.Error(err))
|
||||
return {{.fmtErrorf|raw}}("failed to clear finalizers: %w", err)
|
||||
}
|
||||
} else if !isLeader && isROF {
|
||||
// Append the target method to the logger.
|
||||
logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
|
||||
|
||||
// For finalizing reconcilers, just observe when we aren't the leader.
|
||||
reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
|
||||
}
|
||||
|
||||
// Synchronize the status.
|
||||
if {{.equalitySemantic|raw}}.DeepEqual(original.Status, resource.Status) {
|
||||
// If we didn't change anything then don't call updateStatus.
|
||||
// This is important because the copy we loaded from the injectionInformer's
|
||||
// cache may be stale and we don't want to overwrite a prior update
|
||||
// to status with this stale state.
|
||||
} else if err = r.updateStatus(original, resource); err != nil {
|
||||
logger.Warnw("Failed to update resource status", zap.Error(err))
|
||||
r.Recorder.Eventf(resource, {{.corev1EventTypeWarning|raw}}, "UpdateFailed",
|
||||
"Failed to update status for %q: %v", resource.Name, err)
|
||||
return err
|
||||
if !r.skipStatusUpdates {
|
||||
// Synchronize the status.
|
||||
if {{.equalitySemantic|raw}}.DeepEqual(original.Status, resource.Status) {
|
||||
// If we didn't change anything then don't call updateStatus.
|
||||
// This is important because the copy we loaded from the injectionInformer's
|
||||
// cache may be stale and we don't want to overwrite a prior update
|
||||
// to status with this stale state.
|
||||
} else if !isLeader {
|
||||
logger.Warn("Saw status changes when we aren't the leader!")
|
||||
// TODO: Consider logging the diff at Debug?
|
||||
} else if err = r.updateStatus(original, resource); err != nil {
|
||||
logger.Warnw("Failed to update resource status", zap.Error(err))
|
||||
r.Recorder.Eventf(resource, {{.corev1EventTypeWarning|raw}}, "UpdateFailed",
|
||||
"Failed to update status for %q: %v", resource.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Report the reconciler event, if any.
|
||||
|
@ -463,10 +564,11 @@ func (r *reconcilerImpl) updateFinalizersFiltered(ctx {{.contextContext|raw}}, r
|
|||
{{else}}
|
||||
patcher := r.Client.{{.group}}{{.version}}().{{.type|apiGroup}}(resource.Namespace)
|
||||
{{end}}
|
||||
resource, err = patcher.Patch(resource.Name, {{.typesMergePatchType|raw}}, patch)
|
||||
resourceName := resource.Name
|
||||
resource, err = patcher.Patch(resourceName, {{.typesMergePatchType|raw}}, patch)
|
||||
if err != nil {
|
||||
r.Recorder.Eventf(resource, {{.corev1EventTypeWarning|raw}}, "FinalizerUpdateFailed",
|
||||
"Failed to update finalizers for %q: %v", resource.Name, err)
|
||||
"Failed to update finalizers for %q: %v", resourceName, err)
|
||||
} else {
|
||||
r.Recorder.Eventf(resource, {{.corev1EventTypeNormal|raw}}, "FinalizerUpdate",
|
||||
"Updated %q finalizers", resource.GetName())
|
||||
|
@ -518,4 +620,5 @@ func (r *reconcilerImpl) clearFinalizer(ctx {{.contextContext|raw}}, resource *{
|
|||
// Synchronize the finalizers filtered by r.finalizerName.
|
||||
return r.updateFinalizersFiltered(ctx, resource)
|
||||
}
|
||||
|
||||
`
|
||||
|
|
|
@ -77,6 +77,14 @@ func (g *reconcilerReconcilerStubGenerator) GenerateType(c *generator.Context, t
|
|||
Package: g.reconcilerPkg,
|
||||
Name: "Finalizer",
|
||||
}),
|
||||
"reconcilerReadOnlyInterface": c.Universe.Type(types.Name{
|
||||
Package: g.reconcilerPkg,
|
||||
Name: "ReadOnlyInterface",
|
||||
}),
|
||||
"reconcilerReadOnlyFinalizer": c.Universe.Type(types.Name{
|
||||
Package: g.reconcilerPkg,
|
||||
Name: "ReadOnlyFinalizer",
|
||||
}),
|
||||
"corev1EventTypeNormal": c.Universe.Type(types.Name{
|
||||
Package: "k8s.io/api/core/v1",
|
||||
Name: "EventTypeNormal",
|
||||
|
@ -112,16 +120,26 @@ var _ {{.reconcilerInterface|raw}} = (*Reconciler)(nil)
|
|||
// Optionally check that our Reconciler implements Finalizer
|
||||
//var _ {{.reconcilerFinalizer|raw}} = (*Reconciler)(nil)
|
||||
|
||||
// Optionally check that our Reconciler implements ReadOnlyInterface
|
||||
// Implement this to observe resources even when we are not the leader.
|
||||
//var _ {{.reconcilerReadOnlyInterface|raw}} = (*Reconciler)(nil)
|
||||
|
||||
// Optionally check that our Reconciler implements ReadOnlyFinalizer
|
||||
// Implement this to observe tombstoned resources even when we are not
|
||||
// the leader (best effort).
|
||||
//var _ {{.reconcilerReadOnlyFinalizer|raw}} = (*Reconciler)(nil)
|
||||
|
||||
// ReconcileKind implements Interface.ReconcileKind.
|
||||
func (r *Reconciler) ReconcileKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}} {
|
||||
// TODO: use this if the resource implements InitializeConditions.
|
||||
{{if not .isKRShaped}}// TODO: use this if the resource implements InitializeConditions.
|
||||
// o.Status.InitializeConditions()
|
||||
{{end}}
|
||||
|
||||
// TODO: add custom reconciliation logic here.
|
||||
|
||||
{{if not .isKRShaped}}
|
||||
// TODO: use this if the object has .status.ObservedGeneration.
|
||||
// o.Status.ObservedGeneration = o.Generation
|
||||
// o.Status.ObservedGeneration = o.Generation{{end}}
|
||||
return newReconciledNormal(o.Namespace, o.Name)
|
||||
}
|
||||
|
||||
|
@ -131,4 +149,16 @@ func (r *Reconciler) ReconcileKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}
|
|||
// // TODO: add custom finalization logic here.
|
||||
// return nil
|
||||
//}
|
||||
|
||||
// Optionally, use ObserveKind to observe the resource when we are not the leader.
|
||||
// func (r *Reconciler) ObserveKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}} {
|
||||
// // TODO: add custom observation logic here.
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Optionally, use ObserveFinalizeKind to observe resources being finalized when we are no the leader.
|
||||
//func (r *Reconciler) ObserveFinalizeKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}} {
|
||||
// // TODO: add custom observation logic here.
|
||||
// return nil
|
||||
//}
|
||||
`
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package configmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// ExampleKey signifies a given example configuration in a ConfigMap.
|
||||
ExampleKey = "_example"
|
||||
|
||||
// ExampleChecksumAnnotation is the annotation that stores the computed checksum.
|
||||
ExampleChecksumAnnotation = "knative.dev/example-checksum"
|
||||
)
|
||||
|
||||
var (
|
||||
// Allows for normalizing by collapsing newlines.
|
||||
sequentialNewlines = regexp.MustCompile("(?:\r?\n)+")
|
||||
)
|
||||
|
||||
// Checksum generates a checksum for the example value to be compared against
|
||||
// a respective annotation.
|
||||
// Leading and trailing spaces are ignored.
|
||||
func Checksum(value string) string {
|
||||
return fmt.Sprintf("%08x", crc32.ChecksumIEEE([]byte(sequentialNewlines.ReplaceAllString(strings.TrimSpace(value), `\n`))))
|
||||
}
|
|
@ -26,9 +26,8 @@ import (
|
|||
type ManualWatcher struct {
|
||||
Namespace string
|
||||
|
||||
// Guards mutations to defaultImpl fields
|
||||
m sync.RWMutex
|
||||
|
||||
// Guards observers
|
||||
m sync.RWMutex
|
||||
observers map[string][]Observer
|
||||
}
|
||||
|
||||
|
@ -40,7 +39,7 @@ func (w *ManualWatcher) Watch(name string, o ...Observer) {
|
|||
defer w.m.Unlock()
|
||||
|
||||
if w.observers == nil {
|
||||
w.observers = make(map[string][]Observer, len(o))
|
||||
w.observers = make(map[string][]Observer, 1)
|
||||
}
|
||||
w.observers[name] = append(w.observers[name], o...)
|
||||
}
|
||||
|
@ -58,13 +57,8 @@ func (w *ManualWatcher) OnChange(configMap *corev1.ConfigMap) {
|
|||
// Within our namespace, take the lock and see if there are any registered observers.
|
||||
w.m.RLock()
|
||||
defer w.m.RUnlock()
|
||||
observers, ok := w.observers[configMap.Name]
|
||||
if !ok {
|
||||
return // No observers.
|
||||
}
|
||||
|
||||
// Iterate over the observers and invoke their callbacks.
|
||||
for _, o := range observers {
|
||||
for _, o := range w.observers[configMap.Name] {
|
||||
o(configMap)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
|
@ -76,6 +77,20 @@ func AsInt64(key string, target *int64) ParseFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// AsUint32 parses the value at key as an uint32 into the target, if it exists.
|
||||
func AsUint32(key string, target *uint32) ParseFunc {
|
||||
return func(data map[string]string) error {
|
||||
if raw, ok := data[key]; ok {
|
||||
val, err := strconv.ParseUint(raw, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse %q: %w", key, err)
|
||||
}
|
||||
*target = uint32(val)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AsFloat64 parses the value at key as a float64 into the target, if it exists.
|
||||
func AsFloat64(key string, target *float64) ParseFunc {
|
||||
return func(data map[string]string) error {
|
||||
|
@ -114,6 +129,21 @@ func AsStringSet(key string, target *sets.String) ParseFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// AsQuantity parses the value at key as a *resource.Quantity into the target, if it exists
|
||||
func AsQuantity(key string, target **resource.Quantity) ParseFunc {
|
||||
return func(data map[string]string) error {
|
||||
if raw, ok := data[key]; ok {
|
||||
val, err := resource.ParseQuantity(raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse %q: %w", key, err)
|
||||
}
|
||||
|
||||
*target = &val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parses the given map using the parser functions passed in.
|
||||
func Parse(data map[string]string, parsers ...ParseFunc) error {
|
||||
for _, parse := range parsers {
|
||||
|
|
|
@ -23,9 +23,6 @@ import (
|
|||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// ExampleKey signifies a given example configuration in a ConfigMap.
|
||||
const ExampleKey = "_example"
|
||||
|
||||
// Logger is the interface that UntypedStore expects its logger to conform to.
|
||||
// UntypedStore will log when updates succeed or fail.
|
||||
type Logger interface {
|
||||
|
@ -157,9 +154,7 @@ func (s *UntypedStore) OnConfigChanged(c *corev1.ConfigMap) {
|
|||
s.logger.Infof("%s config %q config was added or updated: %#v", s.name, name, result)
|
||||
storage.Store(result)
|
||||
|
||||
go func() {
|
||||
for _, f := range s.onAfterStore {
|
||||
f(name, result)
|
||||
}
|
||||
}()
|
||||
for _, f := range s.onAfterStore {
|
||||
f(name, result)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package controller
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -35,8 +36,10 @@ import (
|
|||
"k8s.io/client-go/util/workqueue"
|
||||
|
||||
"knative.dev/pkg/kmeta"
|
||||
kle "knative.dev/pkg/leaderelection"
|
||||
"knative.dev/pkg/logging"
|
||||
"knative.dev/pkg/logging/logkey"
|
||||
"knative.dev/pkg/reconciler"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -175,6 +178,10 @@ func FilterWithNameAndNamespace(namespace, name string) func(obj interface{}) bo
|
|||
// Impl is our core controller implementation. It handles queuing and feeding work
|
||||
// from the queue to an implementation of Reconciler.
|
||||
type Impl struct {
|
||||
// Name is the unique name for this controller workqueue within this process.
|
||||
// This is used for surfacing metrics, and per-controller leader election.
|
||||
Name string
|
||||
|
||||
// Reconciler is the workhorse of this controller, it is fed the keys
|
||||
// from the workqueue to process. Public for testing.
|
||||
Reconciler Reconciler
|
||||
|
@ -204,7 +211,9 @@ func NewImpl(r Reconciler, logger *zap.SugaredLogger, workQueueName string) *Imp
|
|||
}
|
||||
|
||||
func NewImplWithStats(r Reconciler, logger *zap.SugaredLogger, workQueueName string, reporter StatsReporter) *Impl {
|
||||
logger = logger.Named(workQueueName)
|
||||
return &Impl{
|
||||
Name: workQueueName,
|
||||
Reconciler: r,
|
||||
WorkQueue: workqueue.NewNamedRateLimitingQueue(
|
||||
workqueue.DefaultControllerRateLimiter(),
|
||||
|
@ -340,6 +349,14 @@ func (c *Impl) EnqueueKey(key types.NamespacedName) {
|
|||
c.logger.Debugf("Adding to queue %s (depth: %d)", safeKey(key), c.WorkQueue.Len())
|
||||
}
|
||||
|
||||
// MaybeEnqueueBucketKey takes a Bucket and namespace/name string and puts it onto the work queue.
|
||||
func (c *Impl) MaybeEnqueueBucketKey(bkt reconciler.Bucket, key types.NamespacedName) {
|
||||
if bkt.Has(key) {
|
||||
c.WorkQueue.Add(key)
|
||||
c.logger.Debugf("Adding to queue %s (depth: %d)", safeKey(key), c.WorkQueue.Len())
|
||||
}
|
||||
}
|
||||
|
||||
// EnqueueKeyAfter takes a namespace/name string and schedules its execution in
|
||||
// the work queue after given delay.
|
||||
func (c *Impl) EnqueueKeyAfter(key types.NamespacedName, delay time.Duration) {
|
||||
|
@ -348,10 +365,12 @@ func (c *Impl) EnqueueKeyAfter(key types.NamespacedName, delay time.Duration) {
|
|||
}
|
||||
|
||||
// RunContext starts the controller's worker threads, the number of which is threadiness.
|
||||
// If the context has been decorated for LeaderElection, then an elector is built and run.
|
||||
// It then blocks until the context is cancelled, at which point it shuts down its
|
||||
// internal work queue and waits for workers to finish processing their current
|
||||
// work items.
|
||||
func (c *Impl) RunContext(ctx context.Context, threadiness int) error {
|
||||
logger := c.logger
|
||||
defer runtime.HandleCrash()
|
||||
sg := sync.WaitGroup{}
|
||||
defer sg.Wait()
|
||||
|
@ -362,8 +381,20 @@ func (c *Impl) RunContext(ctx context.Context, threadiness int) error {
|
|||
}
|
||||
}()
|
||||
|
||||
if la, ok := c.Reconciler.(reconciler.LeaderAware); ok {
|
||||
// Build and execute an elector.
|
||||
le, err := kle.BuildElector(ctx, la, c.Name, c.MaybeEnqueueBucketKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sg.Add(1)
|
||||
go func() {
|
||||
defer sg.Done()
|
||||
le.Run(ctx)
|
||||
}()
|
||||
}
|
||||
|
||||
// Launch workers to process resources that get enqueued to our workqueue.
|
||||
logger := c.logger
|
||||
logger.Info("Starting controller and workers")
|
||||
for i := 0; i < threadiness; i++ {
|
||||
sg.Add(1)
|
||||
|
@ -448,7 +479,7 @@ func (c *Impl) processNextWorkItem() bool {
|
|||
func (c *Impl) handleErr(err error, key types.NamespacedName) {
|
||||
c.logger.Errorw("Reconcile error", zap.Error(err))
|
||||
|
||||
// Re-queue the key if it's an transient error.
|
||||
// Re-queue the key if it's a transient error.
|
||||
// We want to check that the queue is shutting down here
|
||||
// since controller Run might have exited by now (since while this item was
|
||||
// being processed, queue.Len==0).
|
||||
|
@ -483,8 +514,8 @@ func (c *Impl) FilteredGlobalResync(f func(interface{}) bool, si cache.SharedInf
|
|||
}
|
||||
|
||||
// NewPermanentError returns a new instance of permanentError.
|
||||
// Users can wrap an error as permanentError with this in reconcile,
|
||||
// when he does not expect the key to get re-queued.
|
||||
// Users can wrap an error as permanentError with this in reconcile
|
||||
// when they do not expect the key to get re-queued.
|
||||
func NewPermanentError(err error) error {
|
||||
return permanentError{e: err}
|
||||
}
|
||||
|
@ -495,16 +526,21 @@ type permanentError struct {
|
|||
e error
|
||||
}
|
||||
|
||||
// IsPermanentError returns true if given error is permanentError
|
||||
// IsPermanentError returns true if the given error is a permanentError or
|
||||
// wraps a permanentError.
|
||||
func IsPermanentError(err error) bool {
|
||||
switch err.(type) {
|
||||
case permanentError:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return errors.Is(err, permanentError{})
|
||||
}
|
||||
|
||||
// Is implements the Is() interface of error. It returns whether the target
|
||||
// error can be treated as equivalent to a permanentError.
|
||||
func (permanentError) Is(target error) bool {
|
||||
_, ok := target.(permanentError)
|
||||
return ok
|
||||
}
|
||||
|
||||
var _ error = permanentError{}
|
||||
|
||||
// Error implements the Error() interface of error.
|
||||
func (err permanentError) Error() string {
|
||||
if err.e == nil {
|
||||
|
@ -514,6 +550,12 @@ func (err permanentError) Error() string {
|
|||
return err.e.Error()
|
||||
}
|
||||
|
||||
// Unwrap implements the Unwrap() interface of error. It returns the error
|
||||
// wrapped inside permanentError.
|
||||
func (err permanentError) Unwrap() error {
|
||||
return err.e
|
||||
}
|
||||
|
||||
// Informer is the group of methods that a type must implement to be passed to
|
||||
// StartInformers.
|
||||
type Informer interface {
|
||||
|
|
|
@ -31,6 +31,10 @@ type Options struct {
|
|||
// AgentName is the name of the agent this reconciler uses. This overrides
|
||||
// the default controller's agent name.
|
||||
AgentName string
|
||||
|
||||
// SkipStatusUpdates configures this reconciler to either do automated status
|
||||
// updates (default) or skip them if this is set to true.
|
||||
SkipStatusUpdates bool
|
||||
}
|
||||
|
||||
// OptionsFn is a callback method signature that accepts an Impl and returns
|
||||
|
|
|
@ -26,9 +26,7 @@ import (
|
|||
_ "k8s.io/code-generator/cmd/defaulter-gen"
|
||||
_ "k8s.io/code-generator/cmd/informer-gen"
|
||||
_ "k8s.io/code-generator/cmd/lister-gen"
|
||||
_ "k8s.io/kube-openapi/cmd/openapi-gen"
|
||||
|
||||
_ "knative.dev/pkg/codegen/cmd/injection-gen"
|
||||
|
||||
_ "knative.dev/test-infra/scripts"
|
||||
)
|
||||
|
|
|
@ -19,6 +19,12 @@ set -o nounset
|
|||
set -o pipefail
|
||||
|
||||
export GO111MODULE=on
|
||||
# If we run with -mod=vendor here, then generate-groups.sh looks for vendor files in the wrong place.
|
||||
export GOFLAGS=-mod=
|
||||
|
||||
if [ -z "${GOPATH:-}" ]; then
|
||||
export GOPATH=$(go env GOPATH)
|
||||
fi
|
||||
|
||||
source $(dirname $0)/../vendor/knative.dev/test-infra/scripts/library.sh
|
||||
|
||||
|
@ -41,7 +47,7 @@ EXTERNAL_INFORMER_PKG="k8s.io/client-go/informers" \
|
|||
${REPO_ROOT_DIR}/hack/generate-knative.sh "injection" \
|
||||
k8s.io/client-go \
|
||||
k8s.io/api \
|
||||
"admissionregistration:v1beta1 apps:v1 autoscaling:v1,v2beta1 batch:v1,v1beta1 core:v1 rbac:v1" \
|
||||
"admissionregistration:v1beta1,v1 apps:v1 autoscaling:v1,v2beta1 batch:v1,v1beta1 core:v1 rbac:v1" \
|
||||
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
|
||||
--force-genreconciler-kinds "Namespace"
|
||||
|
||||
|
@ -50,7 +56,7 @@ VERSIONED_CLIENTSET_PKG="k8s.io/apiextensions-apiserver/pkg/client/clientset/cli
|
|||
${REPO_ROOT_DIR}/hack/generate-knative.sh "injection" \
|
||||
k8s.io/apiextensions-apiserver/pkg/client \
|
||||
k8s.io/apiextensions-apiserver/pkg/apis \
|
||||
"apiextensions:v1beta1" \
|
||||
"apiextensions:v1beta1,v1" \
|
||||
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
|
||||
--force-genreconciler-kinds "CustomResourceDefinition"
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ cd ${ROOT_DIR}
|
|||
# The list of dependencies that we track at HEAD and periodically
|
||||
# float forward in this repository.
|
||||
FLOATING_DEPS=(
|
||||
"knative.dev/test-infra@release-0.15"
|
||||
"knative.dev/test-infra@release-0.16"
|
||||
)
|
||||
|
||||
# Parse flags to determine any we should pass to dep.
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 The Knative Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
export GO111MODULE=on
|
||||
export K8S_VERSION="$1"
|
||||
|
||||
K8S_DEPS=(
|
||||
"k8s.io/api"
|
||||
"k8s.io/apiextensions-apiserver"
|
||||
"k8s.io/apimachinery"
|
||||
"k8s.io/code-generator"
|
||||
"k8s.io/client-go"
|
||||
)
|
||||
|
||||
|
||||
for dep in "${K8S_DEPS[@]}"
|
||||
do
|
||||
go mod edit \
|
||||
-require="${dep}@${K8S_VERSION}" \
|
||||
-replace="${dep}=${dep}@${K8S_VERSION}"
|
||||
done
|
||||
|
||||
./hack/update-deps.sh
|
||||
|
|
@ -454,14 +454,14 @@ for that resource.
|
|||
|
||||
#### Annotation based common logic
|
||||
|
||||
**krshaped=true may become the default if omitted in the future**
|
||||
**krshapedlogic=false may be used to omit common reconciler logic**
|
||||
|
||||
Reconcilers can handle common logic for resources that conform to the KRShaped
|
||||
interface. This allows the generated code to automatically increment
|
||||
ObservedGeneration.
|
||||
|
||||
```go
|
||||
// +genreconciler:krshapedlogic=true
|
||||
// +genreconciler
|
||||
```
|
||||
|
||||
Setting this annotation will emit the following in the generated reconciler.
|
||||
|
@ -471,7 +471,7 @@ reconciler.PreProcessReconcile(ctx, resource)
|
|||
|
||||
reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
|
||||
|
||||
reconciler.PostProcessReconcile(ctx, resource)
|
||||
reconciler.PostProcessReconcile(ctx, resource, oldResource)
|
||||
```
|
||||
|
||||
#### Stubs
|
||||
|
|
|
@ -25,16 +25,18 @@ func CopyMap(a map[string]string) map[string]string {
|
|||
return ret
|
||||
}
|
||||
|
||||
// UnionMaps returns a map constructed from the union of `a` and `b`,
|
||||
// where value from `b` wins.
|
||||
func UnionMaps(a, b map[string]string) map[string]string {
|
||||
out := make(map[string]string, len(a)+len(b))
|
||||
|
||||
for k, v := range a {
|
||||
out[k] = v
|
||||
// UnionMaps returns a map constructed from the union of input maps.
|
||||
// where values from latter maps win.
|
||||
func UnionMaps(maps ...map[string]string) map[string]string {
|
||||
if len(maps) == 0 {
|
||||
return map[string]string{}
|
||||
}
|
||||
for k, v := range b {
|
||||
out[k] = v
|
||||
out := make(map[string]string, len(maps[0]))
|
||||
|
||||
for _, m := range maps {
|
||||
for k, v := range m {
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package leaderelection
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
||||
|
||||
cm "knative.dev/pkg/configmap"
|
||||
)
|
||||
|
||||
const configMapNameEnv = "CONFIG_LEADERELECTION_NAME"
|
||||
|
||||
// MaxBuckets is the maximum number of buckets to allow users to define.
|
||||
// This is a variable so that it may be customized in the binary entrypoint.
|
||||
var MaxBuckets uint32 = 10
|
||||
|
||||
var validResourceLocks = sets.NewString(resourcelock.LeasesResourceLock)
|
||||
|
||||
// NewConfigFromMap returns a Config for the given map, or an error.
|
||||
func NewConfigFromMap(data map[string]string) (*Config, error) {
|
||||
config := defaultConfig()
|
||||
|
||||
if err := cm.Parse(data,
|
||||
cm.AsString("resourceLock", &config.ResourceLock),
|
||||
|
||||
cm.AsDuration("leaseDuration", &config.LeaseDuration),
|
||||
cm.AsDuration("renewDeadline", &config.RenewDeadline),
|
||||
cm.AsDuration("retryPeriod", &config.RetryPeriod),
|
||||
|
||||
cm.AsUint32("buckets", &config.Buckets),
|
||||
|
||||
// enabledComponents are not validated here, because they are dependent on
|
||||
// the component. Components should provide additional validation for this
|
||||
// field.
|
||||
cm.AsStringSet("enabledComponents", &config.EnabledComponents),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.Buckets < 1 || config.Buckets > MaxBuckets {
|
||||
return nil, fmt.Errorf("buckets: value must be between %d <= %d <= %d", 1, config.Buckets, MaxBuckets)
|
||||
}
|
||||
if !validResourceLocks.Has(config.ResourceLock) {
|
||||
return nil, fmt.Errorf(`resourceLock: invalid value %q: valid values are "leases"`, config.ResourceLock)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// NewConfigFromConfigMap returns a new Config from the given ConfigMap.
|
||||
func NewConfigFromConfigMap(configMap *corev1.ConfigMap) (*Config, error) {
|
||||
if configMap == nil {
|
||||
config := defaultConfig()
|
||||
return config, nil
|
||||
}
|
||||
return NewConfigFromMap(configMap.Data)
|
||||
}
|
||||
|
||||
// Config represents the leader election config for a set of components
|
||||
// contained within a single namespace. Typically these will correspond to a
|
||||
// single source repository, viz: serving or eventing.
|
||||
type Config struct {
|
||||
ResourceLock string
|
||||
Buckets uint32
|
||||
LeaseDuration time.Duration
|
||||
RenewDeadline time.Duration
|
||||
RetryPeriod time.Duration
|
||||
EnabledComponents sets.String
|
||||
}
|
||||
|
||||
func (c *Config) GetComponentConfig(name string) ComponentConfig {
|
||||
if c.EnabledComponents.Has(name) {
|
||||
return ComponentConfig{
|
||||
Component: name,
|
||||
LeaderElect: true,
|
||||
Buckets: c.Buckets,
|
||||
ResourceLock: c.ResourceLock,
|
||||
LeaseDuration: c.LeaseDuration,
|
||||
RenewDeadline: c.RenewDeadline,
|
||||
RetryPeriod: c.RetryPeriod,
|
||||
}
|
||||
}
|
||||
|
||||
return defaultComponentConfig(name)
|
||||
}
|
||||
|
||||
func defaultConfig() *Config {
|
||||
return &Config{
|
||||
ResourceLock: "leases",
|
||||
Buckets: 1,
|
||||
LeaseDuration: 15 * time.Second,
|
||||
RenewDeadline: 10 * time.Second,
|
||||
RetryPeriod: 2 * time.Second,
|
||||
EnabledComponents: sets.NewString(),
|
||||
}
|
||||
}
|
||||
|
||||
// ComponentConfig represents the leader election config for a single component.
|
||||
type ComponentConfig struct {
|
||||
Component string
|
||||
LeaderElect bool
|
||||
Buckets uint32
|
||||
ResourceLock string
|
||||
LeaseDuration time.Duration
|
||||
RenewDeadline time.Duration
|
||||
RetryPeriod time.Duration
|
||||
}
|
||||
|
||||
// statefulSetID is a envconfig Decodable controller ordinal and name.
|
||||
type statefulSetID struct {
|
||||
ssName string
|
||||
ordinal int
|
||||
}
|
||||
|
||||
func (ssID *statefulSetID) Decode(v string) error {
|
||||
if i := strings.LastIndex(v, "-"); i != -1 {
|
||||
ui, err := strconv.ParseUint(v[i+1:], 10, 64)
|
||||
ssID.ordinal = int(ui)
|
||||
ssID.ssName = v[:i]
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%q is not a valid stateful set controller ordinal", v)
|
||||
}
|
||||
|
||||
var _ envconfig.Decoder = (*statefulSetID)(nil)
|
||||
|
||||
// statefulSetConfig represents the required information for a StatefulSet service.
|
||||
type statefulSetConfig struct {
|
||||
StatefulSetID statefulSetID `envconfig:"STATEFUL_CONTROLLER_ORDINAL" required:"true"`
|
||||
ServiceName string `envconfig:"STATEFUL_SERVICE_NAME" required:"true"`
|
||||
Port string `envconfig:"STATEFUL_SERVICE_PORT" default:"80"`
|
||||
Protocol string `envconfig:"STATEFUL_SERVICE_PROTOCOL" default:"http"`
|
||||
}
|
||||
|
||||
// newStatefulSetConfig builds a stateful set LE config.
|
||||
func newStatefulSetConfig() (*statefulSetConfig, error) {
|
||||
ssc := &statefulSetConfig{}
|
||||
if err := envconfig.Process("", ssc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ssc, nil
|
||||
}
|
||||
|
||||
func defaultComponentConfig(name string) ComponentConfig {
|
||||
return ComponentConfig{
|
||||
Component: name,
|
||||
LeaderElect: false,
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigMapName returns the name of the configmap to read for leader election
|
||||
// settings.
|
||||
func ConfigMapName() string {
|
||||
cm := os.Getenv(configMapNameEnv)
|
||||
if cm == "" {
|
||||
return "config-leader-election"
|
||||
}
|
||||
return cm
|
||||
}
|
||||
|
||||
// UniqueID returns a unique ID for use with a leader elector that prevents from
|
||||
// pods running on the same host from colliding with one another.
|
||||
func UniqueID() (string, error) {
|
||||
id, err := os.Hostname()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return id + "_" + string(uuid.NewUUID()), nil
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package leaderelection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/leaderelection"
|
||||
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
||||
"knative.dev/pkg/logging"
|
||||
"knative.dev/pkg/network"
|
||||
"knative.dev/pkg/reconciler"
|
||||
"knative.dev/pkg/system"
|
||||
)
|
||||
|
||||
// WithDynamicLeaderElectorBuilder sets up the statefulset elector based on environment,
|
||||
// falling back on the standard elector.
|
||||
func WithDynamicLeaderElectorBuilder(ctx context.Context, kc kubernetes.Interface, cc ComponentConfig) context.Context {
|
||||
logger := logging.FromContext(ctx)
|
||||
ssc, err := newStatefulSetConfig()
|
||||
if err == nil {
|
||||
logger.Info("Running with StatefulSet leader election")
|
||||
return withStatefulSetElectorBuilder(ctx, cc, *ssc)
|
||||
}
|
||||
logger.Info("Running with Standard leader election")
|
||||
return WithStandardLeaderElectorBuilder(ctx, kc, cc)
|
||||
}
|
||||
|
||||
// WithStandardLeaderElectorBuilder infuses a context with the ability to build
|
||||
// LeaderElectors with the provided component configuration acquiring resource
|
||||
// locks via the provided kubernetes client.
|
||||
func WithStandardLeaderElectorBuilder(ctx context.Context, kc kubernetes.Interface, cc ComponentConfig) context.Context {
|
||||
return context.WithValue(ctx, builderKey{}, &standardBuilder{
|
||||
kc: kc,
|
||||
lec: cc,
|
||||
})
|
||||
}
|
||||
|
||||
// withStatefulSetElectorBuilder infuses a context with the ability to build
|
||||
// Electors which are assigned leadership based on the StatefulSet ordinal from
|
||||
// the provided component configuration.
|
||||
func withStatefulSetElectorBuilder(ctx context.Context, cc ComponentConfig, ssc statefulSetConfig) context.Context {
|
||||
return context.WithValue(ctx, builderKey{}, &statefulSetBuilder{
|
||||
lec: cc,
|
||||
ssc: ssc,
|
||||
})
|
||||
}
|
||||
|
||||
// HasLeaderElection returns whether there is leader election configuration
|
||||
// associated with the context
|
||||
func HasLeaderElection(ctx context.Context) bool {
|
||||
val := ctx.Value(builderKey{})
|
||||
return val != nil
|
||||
}
|
||||
|
||||
// Elector is the interface for running a leader elector.
|
||||
type Elector interface {
|
||||
Run(context.Context)
|
||||
}
|
||||
|
||||
// BuildElector builds a leaderelection.LeaderElector for the named LeaderAware
|
||||
// reconciler using a builder added to the context via WithStandardLeaderElectorBuilder.
|
||||
func BuildElector(ctx context.Context, la reconciler.LeaderAware, name string, enq func(reconciler.Bucket, types.NamespacedName)) (Elector, error) {
|
||||
if val := ctx.Value(builderKey{}); val != nil {
|
||||
switch builder := val.(type) {
|
||||
case *standardBuilder:
|
||||
return builder.buildElector(ctx, la, name, enq)
|
||||
case *statefulSetBuilder:
|
||||
return builder.buildElector(ctx, la, enq)
|
||||
}
|
||||
}
|
||||
|
||||
return &unopposedElector{
|
||||
la: la,
|
||||
bkt: reconciler.UniversalBucket(),
|
||||
enq: enq,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type builderKey struct{}
|
||||
|
||||
type standardBuilder struct {
|
||||
kc kubernetes.Interface
|
||||
lec ComponentConfig
|
||||
}
|
||||
|
||||
func (b *standardBuilder) buildElector(ctx context.Context, la reconciler.LeaderAware,
|
||||
name string, enq func(reconciler.Bucket, types.NamespacedName)) (Elector, error) {
|
||||
logger := logging.FromContext(ctx)
|
||||
|
||||
id, err := UniqueID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buckets := make([]Elector, 0, b.lec.Buckets)
|
||||
for i := uint32(0); i < b.lec.Buckets; i++ {
|
||||
bkt := &bucket{
|
||||
// The resource name is the lowercase:
|
||||
// {component}.{workqueue}.{index}-of-{total}
|
||||
name: strings.ToLower(fmt.Sprintf("%s.%s.%02d-of-%02d", b.lec.Component, name, i, b.lec.Buckets)),
|
||||
index: i,
|
||||
total: b.lec.Buckets,
|
||||
}
|
||||
|
||||
rl, err := resourcelock.New(b.lec.ResourceLock,
|
||||
system.Namespace(), // use namespace we are running in
|
||||
bkt.Name(),
|
||||
b.kc.CoreV1(),
|
||||
b.kc.CoordinationV1(),
|
||||
resourcelock.ResourceLockConfig{
|
||||
Identity: id,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Infof("%s will run in leader-elected mode with id %q", bkt.Name(), rl.Identity())
|
||||
|
||||
le, err := leaderelection.NewLeaderElector(leaderelection.LeaderElectionConfig{
|
||||
Lock: rl,
|
||||
LeaseDuration: b.lec.LeaseDuration,
|
||||
RenewDeadline: b.lec.RenewDeadline,
|
||||
RetryPeriod: b.lec.RetryPeriod,
|
||||
Callbacks: leaderelection.LeaderCallbacks{
|
||||
OnStartedLeading: func(context.Context) {
|
||||
logger.Infof("%q has started leading %q", rl.Identity(), bkt.Name())
|
||||
if err := la.Promote(bkt, enq); err != nil {
|
||||
// TODO(mattmoor): We expect this to effectively never happen,
|
||||
// but if it does, we should support wrapping `le` in an elector
|
||||
// we can cancel here.
|
||||
logger.Fatalf("%q failed to Promote: %v", rl.Identity(), err)
|
||||
}
|
||||
},
|
||||
OnStoppedLeading: func() {
|
||||
logger.Infof("%q has stopped leading %q", rl.Identity(), bkt.Name())
|
||||
la.Demote(bkt)
|
||||
},
|
||||
},
|
||||
ReleaseOnCancel: true,
|
||||
Name: rl.Identity(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: use health check watchdog, knative/pkg#1048
|
||||
// if lec.WatchDog != nil {
|
||||
// lec.WatchDog.SetLeaderElection(le)
|
||||
// }
|
||||
buckets = append(buckets, &runUntilCancelled{Elector: le})
|
||||
}
|
||||
return &runAll{les: buckets}, nil
|
||||
}
|
||||
|
||||
type statefulSetBuilder struct {
|
||||
lec ComponentConfig
|
||||
ssc statefulSetConfig
|
||||
}
|
||||
|
||||
func (b *statefulSetBuilder) buildElector(ctx context.Context, la reconciler.LeaderAware, enq func(reconciler.Bucket, types.NamespacedName)) (Elector, error) {
|
||||
logger := logging.FromContext(ctx)
|
||||
ordinal := uint32(b.ssc.StatefulSetID.ordinal)
|
||||
logger.Infof("%s will run in StatefulSet ordinal assignement mode with ordinal %d",
|
||||
b.lec.Component, ordinal)
|
||||
|
||||
return &unopposedElector{
|
||||
bkt: &bucket{
|
||||
// The name is the full pod DNS of the owner pod of this bucket.
|
||||
name: fmt.Sprintf("%s://%s-%d.%s.%s.svc.%s:%s", b.ssc.Protocol,
|
||||
b.ssc.StatefulSetID.ssName, ordinal, b.ssc.ServiceName,
|
||||
system.Namespace(), network.GetClusterDomainName(), b.ssc.Port),
|
||||
index: ordinal,
|
||||
total: b.lec.Buckets,
|
||||
},
|
||||
la: la,
|
||||
enq: enq,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// unopposedElector promotes when run without needing to be elected.
|
||||
type unopposedElector struct {
|
||||
bkt reconciler.Bucket
|
||||
la reconciler.LeaderAware
|
||||
enq func(reconciler.Bucket, types.NamespacedName)
|
||||
}
|
||||
|
||||
// Run implements Elector
|
||||
func (ue *unopposedElector) Run(ctx context.Context) {
|
||||
ue.la.Promote(ue.bkt, ue.enq)
|
||||
}
|
||||
|
||||
type runAll struct {
|
||||
les []Elector
|
||||
}
|
||||
|
||||
// Run implements Elector
|
||||
func (ra *runAll) Run(ctx context.Context) {
|
||||
sg := sync.WaitGroup{}
|
||||
defer sg.Wait()
|
||||
|
||||
for _, le := range ra.les {
|
||||
sg.Add(1)
|
||||
go func(le Elector) {
|
||||
defer sg.Done()
|
||||
le.Run(ctx)
|
||||
}(le)
|
||||
}
|
||||
}
|
||||
|
||||
// runUntilCancelled wraps a single-term Elector into one that runs until
|
||||
// the passed context is cancelled.
|
||||
type runUntilCancelled struct {
|
||||
// Elector is a single-term elector as we get from K8s leaderelection package.
|
||||
Elector
|
||||
}
|
||||
|
||||
// Run implements Elector
|
||||
func (ruc *runUntilCancelled) Run(ctx context.Context) {
|
||||
// Turn the single-term elector into a continuous election cycle.
|
||||
for {
|
||||
ruc.Elector.Run(ctx)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return // Run quit because context was cancelled, we are done!
|
||||
default:
|
||||
// Context wasn't cancelled, start over.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type bucket struct {
|
||||
name string
|
||||
|
||||
// We are bucket {index} of {total}
|
||||
index uint32
|
||||
total uint32
|
||||
}
|
||||
|
||||
var _ reconciler.Bucket = (*bucket)(nil)
|
||||
|
||||
// Name implements reconciler.Bucket
|
||||
func (b *bucket) Name() string {
|
||||
return b.name
|
||||
}
|
||||
|
||||
// Has implements reconciler.Bucket
|
||||
func (b *bucket) Has(nn types.NamespacedName) bool {
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(nn.Namespace + "." + nn.Name))
|
||||
ii := h.Sum32() % b.total
|
||||
return b.index == ii
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package leaderelection
|
||||
|
||||
// ControllerOrdinal tries to get ordinal from the pod name of a StatefulSet,
|
||||
// which is provided from the environment variable CONTROLLER_ORDINAL.
|
||||
func ControllerOrdinal() (int, error) {
|
||||
ssc, err := newStatefulSetConfig()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return ssc.StatefulSetID.ordinal, nil
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
# The OWNERS file is used by prow to automatically merge approved PRs.
|
||||
|
||||
approvers:
|
||||
- configmap-approvers
|
|
@ -31,9 +31,8 @@ import (
|
|||
"knative.dev/pkg/logging/logkey"
|
||||
)
|
||||
|
||||
const ConfigMapNameEnv = "CONFIG_LOGGING_NAME"
|
||||
|
||||
const (
|
||||
configMapNameEnv = "CONFIG_LOGGING_NAME"
|
||||
loggerConfigKey = "zap-logger-config"
|
||||
fallbackLoggerName = "fallback-logger"
|
||||
)
|
||||
|
@ -233,7 +232,7 @@ func UpdateLevelFromConfigMap(logger *zap.SugaredLogger, atomicLevel zap.AtomicL
|
|||
|
||||
// ConfigMapName gets the name of the logging ConfigMap
|
||||
func ConfigMapName() string {
|
||||
cm := os.Getenv(ConfigMapNameEnv)
|
||||
cm := os.Getenv(configMapNameEnv)
|
||||
if cm == "" {
|
||||
return "config-logging"
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
"knative.dev/pkg/logging/logkey"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
@ -49,10 +50,10 @@ func StringSet(s sets.String) zapcore.ObjectMarshalerFunc {
|
|||
// logger.Info("Enqueuing", zap.Object("key", logging.NamespacedName(n)))
|
||||
func NamespacedName(n types.NamespacedName) zapcore.ObjectMarshalerFunc {
|
||||
return func(enc zapcore.ObjectEncoder) error {
|
||||
if n.Namespace != "" {
|
||||
enc.AddString("key", n.Name)
|
||||
if n.Namespace == "" {
|
||||
enc.AddString(logkey.Key, n.Name)
|
||||
} else {
|
||||
enc.AddString("key", n.Namespace+"/"+n.Name)
|
||||
enc.AddString(logkey.Key, n.Namespace+"/"+n.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -37,39 +37,44 @@ import (
|
|||
type metricsBackend string
|
||||
|
||||
const (
|
||||
// BackendDestinationKey points to the config map entry key for metrics backend destination.
|
||||
BackendDestinationKey = "metrics.backend-destination"
|
||||
// DomainEnv points to the metrics domain env var.
|
||||
DomainEnv = "METRICS_DOMAIN"
|
||||
|
||||
// The following keys are used to configure metrics reporting.
|
||||
// See https://github.com/knative/serving/blob/master/config/config-observability.yaml
|
||||
// for details.
|
||||
AllowStackdriverCustomMetricsKey = "metrics.allow-stackdriver-custom-metrics"
|
||||
BackendDestinationKey = "metrics.backend-destination"
|
||||
ReportingPeriodKey = "metrics.reporting-period-seconds"
|
||||
StackdriverCustomMetricSubDomainKey = "metrics.stackdriver-custom-metrics-subdomain"
|
||||
allowStackdriverCustomMetricsKey = "metrics.allow-stackdriver-custom-metrics"
|
||||
collectorAddressKey = "metrics.opencensus-address"
|
||||
collectorSecureKey = "metrics.opencensus-require-tls"
|
||||
reportingPeriodKey = "metrics.reporting-period-seconds"
|
||||
|
||||
// Stackdriver client configuration keys
|
||||
StackdriverProjectIDKey = "metrics.stackdriver-project-id"
|
||||
StackdriverGCPLocationKey = "metrics.stackdriver-gcp-location"
|
||||
StackdriverClusterNameKey = "metrics.stackdriver-cluster-name"
|
||||
StackdriverUseSecretKey = "metrics.stackdriver-use-secret"
|
||||
|
||||
DomainEnv = "METRICS_DOMAIN"
|
||||
|
||||
// Stackdriver is used for Stackdriver backend
|
||||
Stackdriver metricsBackend = "stackdriver"
|
||||
// Prometheus is used for Prometheus backend
|
||||
Prometheus metricsBackend = "prometheus"
|
||||
// OpenCensus is used to export to the OpenCensus Agent / Collector,
|
||||
// which can send to many other services.
|
||||
OpenCensus metricsBackend = "opencensus"
|
||||
// None is used to export, well, nothing.
|
||||
None metricsBackend = "none"
|
||||
stackdriverClusterNameKey = "metrics.stackdriver-cluster-name"
|
||||
stackdriverCustomMetricSubDomainKey = "metrics.stackdriver-custom-metrics-subdomain"
|
||||
stackdriverGCPLocationKey = "metrics.stackdriver-gcp-location"
|
||||
stackdriverProjectIDKey = "metrics.stackdriver-project-id"
|
||||
stackdriverUseSecretKey = "metrics.stackdriver-use-secret"
|
||||
|
||||
defaultBackendEnvName = "DEFAULT_METRICS_BACKEND"
|
||||
|
||||
CollectorAddressKey = "metrics.opencensus-address"
|
||||
CollectorSecureKey = "metrics.opencensus-require-tls"
|
||||
|
||||
defaultPrometheusPort = 9090
|
||||
maxPrometheusPort = 65535
|
||||
minPrometheusPort = 1024
|
||||
prometheusPortEnvName = "METRICS_PROMETHEUS_PORT"
|
||||
)
|
||||
|
||||
// Metrics backend "enum".
|
||||
const (
|
||||
// stackdriver is used for Stackdriver backend
|
||||
stackdriver metricsBackend = "stackdriver"
|
||||
// prometheus is used for Prometheus backend
|
||||
prometheus metricsBackend = "prometheus"
|
||||
// openCensus is used to export to the OpenCensus Agent / Collector,
|
||||
// which can send to many other services.
|
||||
openCensus metricsBackend = "opencensus"
|
||||
// none is used to export, well, nothing.
|
||||
none metricsBackend = "none"
|
||||
)
|
||||
|
||||
type metricsConfig struct {
|
||||
|
@ -141,10 +146,10 @@ type StackdriverClientConfig struct {
|
|||
// NewStackdriverClientConfigFromMap creates a stackdriverClientConfig from the given map
|
||||
func NewStackdriverClientConfigFromMap(config map[string]string) *StackdriverClientConfig {
|
||||
return &StackdriverClientConfig{
|
||||
ProjectID: config[StackdriverProjectIDKey],
|
||||
GCPLocation: config[StackdriverGCPLocationKey],
|
||||
ClusterName: config[StackdriverClusterNameKey],
|
||||
UseSecret: strings.EqualFold(config[StackdriverUseSecretKey], "true"),
|
||||
ProjectID: config[stackdriverProjectIDKey],
|
||||
GCPLocation: config[stackdriverGCPLocationKey],
|
||||
ClusterName: config[stackdriverClusterNameKey],
|
||||
UseSecret: strings.EqualFold(config[stackdriverUseSecretKey], "true"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,7 +188,7 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
|
|||
backend := os.Getenv(defaultBackendEnvName)
|
||||
if backend == "" {
|
||||
// Use Prometheus if DEFAULT_METRICS_BACKEND does not exist or is empty
|
||||
backend = string(Prometheus)
|
||||
backend = string(prometheus)
|
||||
}
|
||||
// Override backend if it is set in the config map.
|
||||
if backendFromConfig, ok := m[BackendDestinationKey]; ok {
|
||||
|
@ -191,18 +196,18 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
|
|||
}
|
||||
lb := metricsBackend(strings.ToLower(backend))
|
||||
switch lb {
|
||||
case Stackdriver, Prometheus, OpenCensus:
|
||||
case stackdriver, prometheus, openCensus:
|
||||
mc.backendDestination = lb
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported metrics backend value %q", backend)
|
||||
}
|
||||
|
||||
if mc.backendDestination == OpenCensus {
|
||||
mc.collectorAddress = ops.ConfigMap[CollectorAddressKey]
|
||||
if isSecure := ops.ConfigMap[CollectorSecureKey]; isSecure != "" {
|
||||
if mc.backendDestination == openCensus {
|
||||
mc.collectorAddress = ops.ConfigMap[collectorAddressKey]
|
||||
if isSecure := ops.ConfigMap[collectorSecureKey]; isSecure != "" {
|
||||
var err error
|
||||
if mc.requireSecure, err = strconv.ParseBool(isSecure); err != nil {
|
||||
return nil, fmt.Errorf("invalid %s value %q", CollectorSecureKey, isSecure)
|
||||
return nil, fmt.Errorf("invalid %s value %q", collectorSecureKey, isSecure)
|
||||
}
|
||||
|
||||
if mc.requireSecure {
|
||||
|
@ -214,21 +219,28 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
|
|||
}
|
||||
}
|
||||
|
||||
if mc.backendDestination == Prometheus {
|
||||
if mc.backendDestination == prometheus {
|
||||
pp := ops.PrometheusPort
|
||||
if pp == 0 {
|
||||
pp = defaultPrometheusPort
|
||||
var err error
|
||||
pp, err = prometheusPort()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to determine Prometheus port: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if pp < minPrometheusPort || pp > maxPrometheusPort {
|
||||
return nil, fmt.Errorf("invalid port %v, should between %v and %v", pp, minPrometheusPort, maxPrometheusPort)
|
||||
return nil, fmt.Errorf("invalid port %d, should be between %d and %d",
|
||||
pp, minPrometheusPort, maxPrometheusPort)
|
||||
}
|
||||
|
||||
mc.prometheusPort = pp
|
||||
}
|
||||
|
||||
// If stackdriverClientConfig is not provided for stackdriver backend destination, OpenCensus will try to
|
||||
// use the application default credentials. If that is not available, Opencensus would fail to create the
|
||||
// metrics exporter.
|
||||
if mc.backendDestination == Stackdriver {
|
||||
if mc.backendDestination == stackdriver {
|
||||
scc := NewStackdriverClientConfigFromMap(m)
|
||||
mc.stackdriverClientConfig = *scc
|
||||
mc.isStackdriverBackend = true
|
||||
|
@ -236,15 +248,15 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
|
|||
var err error
|
||||
mc.stackdriverMetricTypePrefix = path.Join(mc.domain, mc.component)
|
||||
|
||||
customMetricsSubDomain := m[StackdriverCustomMetricSubDomainKey]
|
||||
customMetricsSubDomain := m[stackdriverCustomMetricSubDomainKey]
|
||||
if customMetricsSubDomain == "" {
|
||||
customMetricsSubDomain = defaultCustomMetricSubDomain
|
||||
}
|
||||
mc.stackdriverCustomMetricTypePrefix = path.Join(customMetricTypePrefix, customMetricsSubDomain, mc.component)
|
||||
if ascmStr := m[AllowStackdriverCustomMetricsKey]; ascmStr != "" {
|
||||
if ascmStr := m[allowStackdriverCustomMetricsKey]; ascmStr != "" {
|
||||
allowCustomMetrics, err = strconv.ParseBool(ascmStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid %s value %q", AllowStackdriverCustomMetricsKey, ascmStr)
|
||||
return nil, fmt.Errorf("invalid %s value %q", allowStackdriverCustomMetricsKey, ascmStr)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,15 +301,15 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
|
|||
// For Prometheus, we will use a lower value since the exporter doesn't
|
||||
// push anything but just responds to pull requests, and shorter durations
|
||||
// do not really hurt the performance and we rely on the scraping configuration.
|
||||
if repStr, ok := m[ReportingPeriodKey]; ok && repStr != "" {
|
||||
if repStr, ok := m[reportingPeriodKey]; ok && repStr != "" {
|
||||
repInt, err := strconv.Atoi(repStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid %s value %q", ReportingPeriodKey, repStr)
|
||||
return nil, fmt.Errorf("invalid %s value %q", reportingPeriodKey, repStr)
|
||||
}
|
||||
mc.reportingPeriod = time.Duration(repInt) * time.Second
|
||||
} else if mc.backendDestination == Stackdriver {
|
||||
} else if mc.backendDestination == stackdriver {
|
||||
mc.reportingPeriod = 60 * time.Second
|
||||
} else if mc.backendDestination == Prometheus {
|
||||
} else if mc.backendDestination == prometheus {
|
||||
mc.reportingPeriod = 5 * time.Second
|
||||
}
|
||||
|
||||
|
@ -327,6 +339,25 @@ import (
|
|||
)`, DomainEnv, DomainEnv))
|
||||
}
|
||||
|
||||
// prometheusPort returns the TCP port number configured via the environment
|
||||
// for the Prometheus metrics exporter if it's set, a default value otherwise.
|
||||
// No validation is performed on the port value, other than ensuring that value
|
||||
// is a valid port number (16-bit unsigned integer).
|
||||
func prometheusPort() (int, error) {
|
||||
ppStr := os.Getenv(prometheusPortEnvName)
|
||||
if ppStr == "" {
|
||||
return defaultPrometheusPort, nil
|
||||
}
|
||||
|
||||
pp, err := strconv.ParseUint(ppStr, 10, 16)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("the environment variable %q could not be parsed as a port number: %w",
|
||||
prometheusPortEnvName, err)
|
||||
}
|
||||
|
||||
return int(pp), nil
|
||||
}
|
||||
|
||||
// JsonToMetricsOptions converts a json string of a
|
||||
// ExporterOptions. Returns a non-nil ExporterOptions always.
|
||||
func JsonToMetricsOptions(jsonOpts string) (*ExporterOptions, error) {
|
||||
|
|
|
@ -29,10 +29,10 @@ const (
|
|||
DefaultLogURLTemplate = "http://localhost:8001/api/v1/namespaces/knative-monitoring/services/kibana-logging/proxy/app/kibana#/discover?_a=(query:(match:(kubernetes.labels.knative-dev%2FrevisionUID:(query:'${REVISION_UID}',type:phrase))))"
|
||||
|
||||
// The following is used to set the default metrics backend
|
||||
DefaultRequestMetricsBackend = "prometheus"
|
||||
defaultRequestMetricsBackend = "prometheus"
|
||||
|
||||
// The env var name for config-observability
|
||||
ConfigMapNameEnv = "CONFIG_OBSERVABILITY_NAME"
|
||||
configMapNameEnv = "CONFIG_OBSERVABILITY_NAME"
|
||||
)
|
||||
|
||||
// ObservabilityConfig contains the configuration defined in the observability ConfigMap.
|
||||
|
@ -64,7 +64,7 @@ type ObservabilityConfig struct {
|
|||
func defaultConfig() *ObservabilityConfig {
|
||||
return &ObservabilityConfig{
|
||||
LoggingURLTemplate: DefaultLogURLTemplate,
|
||||
RequestMetricsBackend: DefaultRequestMetricsBackend,
|
||||
RequestMetricsBackend: defaultRequestMetricsBackend,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ func NewObservabilityConfigFromConfigMap(configMap *corev1.ConfigMap) (*Observab
|
|||
|
||||
// ConfigMapName gets the name of the metrics ConfigMap
|
||||
func ConfigMapName() string {
|
||||
cm := os.Getenv(ConfigMapNameEnv)
|
||||
cm := os.Getenv(configMapNameEnv)
|
||||
if cm == "" {
|
||||
return "config-observability"
|
||||
}
|
||||
|
|
|
@ -85,6 +85,26 @@ func UpdateExporterFromConfigMap(component string, logger *zap.SugaredLogger) fu
|
|||
return ConfigMapWatcher(component, nil, logger)
|
||||
}
|
||||
|
||||
// RegisterResourceView is similar to view.Register(), except that it will
|
||||
// register the view across all Resources tracked by the system, rather than
|
||||
// simply the default view.
|
||||
// this is a placeholder for real implementation in https://github.com/knative/pkg/pull/1392.
|
||||
// That PR will introduce a breaking change, as we need to convert some view.Register to RegisterResourceView in
|
||||
// all callers, in serving, eventing, and eventing-contrib.
|
||||
// Since that PR is huge, a better approach is to introduce the breaking change up front, by creating this
|
||||
// passthrough method, and fixing all the callers before merging that PR.
|
||||
func RegisterResourceView(views ...*view.View) error {
|
||||
return view.Register(views...)
|
||||
}
|
||||
|
||||
// UnregisterResourceView is similar to view.unRegister(), except that it will
|
||||
// unregister the view across all Resources tracked by the system, rather than
|
||||
// simply the default view.
|
||||
// this is a placeholder for real implementation in https://github.com/knative/pkg/pull/1392.
|
||||
func UnregisterResourceView(views ...*view.View) {
|
||||
view.Unregister(views...)
|
||||
}
|
||||
|
||||
// ConfigMapWatcher returns a helper func which updates the exporter configuration based on
|
||||
// values in the supplied ConfigMap. This method captures a corev1.SecretLister which is used
|
||||
// to configure mTLS with the opencensus agent.
|
||||
|
@ -176,11 +196,11 @@ func isNewExporterRequired(newConfig *metricsConfig) bool {
|
|||
|
||||
// If the OpenCensus address has changed, restart the exporter.
|
||||
// TODO(evankanderson): Should we just always restart the opencensus agent?
|
||||
if newConfig.backendDestination == OpenCensus {
|
||||
if newConfig.backendDestination == openCensus {
|
||||
return newConfig.collectorAddress != cc.collectorAddress || newConfig.requireSecure != cc.requireSecure
|
||||
}
|
||||
|
||||
return newConfig.backendDestination == Stackdriver && newConfig.stackdriverClientConfig != cc.stackdriverClientConfig
|
||||
return newConfig.backendDestination == stackdriver && newConfig.stackdriverClientConfig != cc.stackdriverClientConfig
|
||||
}
|
||||
|
||||
// newMetricsExporter gets a metrics exporter based on the config.
|
||||
|
@ -198,13 +218,13 @@ func newMetricsExporter(config *metricsConfig, logger *zap.SugaredLogger) (view.
|
|||
var err error
|
||||
var e view.Exporter
|
||||
switch config.backendDestination {
|
||||
case OpenCensus:
|
||||
case openCensus:
|
||||
e, err = newOpenCensusExporter(config, logger)
|
||||
case Stackdriver:
|
||||
case stackdriver:
|
||||
e, err = newStackdriverExporter(config, logger)
|
||||
case Prometheus:
|
||||
case prometheus:
|
||||
e, err = newPrometheusExporter(config, logger)
|
||||
case None:
|
||||
case none:
|
||||
e, err = nil, nil
|
||||
default:
|
||||
err = fmt.Errorf("unsupported metrics backend %v", config.backendDestination)
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
"net/http"
|
||||
"sync"
|
||||
|
||||
"contrib.go.opencensus.io/exporter/prometheus"
|
||||
prom "contrib.go.opencensus.io/exporter/prometheus"
|
||||
"go.opencensus.io/stats/view"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -32,7 +32,7 @@ var (
|
|||
)
|
||||
|
||||
func newPrometheusExporter(config *metricsConfig, logger *zap.SugaredLogger) (view.Exporter, error) {
|
||||
e, err := prometheus.NewExporter(prometheus.Options{Namespace: config.component})
|
||||
e, err := prom.NewExporter(prom.Options{Namespace: config.component})
|
||||
if err != nil {
|
||||
logger.Errorw("Failed to create the Prometheus exporter.", zap.Error(err))
|
||||
return nil, err
|
||||
|
@ -61,7 +61,7 @@ func resetCurPromSrv() {
|
|||
}
|
||||
}
|
||||
|
||||
func startNewPromSrv(e *prometheus.Exporter, port int) *http.Server {
|
||||
func startNewPromSrv(e *prom.Exporter, port int) *http.Server {
|
||||
sm := http.NewServeMux()
|
||||
sm.Handle("/metrics", e)
|
||||
curPromSrvMux.Lock()
|
||||
|
|
|
@ -40,7 +40,7 @@ func RecordBatch(ctx context.Context, mss ...stats.Measurement) {
|
|||
// be used to create a view.Distribution.
|
||||
func Buckets125(low, high float64) []float64 {
|
||||
buckets := []float64{low}
|
||||
for last := low; last < high; last = last * 10 {
|
||||
for last := low; last < high; last *= 10 {
|
||||
buckets = append(buckets, 2*last, 5*last, 10*last)
|
||||
}
|
||||
return buckets
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
"path"
|
||||
"sync"
|
||||
|
||||
"contrib.go.opencensus.io/exporter/stackdriver"
|
||||
sd "contrib.go.opencensus.io/exporter/stackdriver"
|
||||
"contrib.go.opencensus.io/exporter/stackdriver/monitoredresource"
|
||||
"go.opencensus.io/metric/metricdata"
|
||||
"go.opencensus.io/stats/view"
|
||||
|
@ -60,7 +60,7 @@ var (
|
|||
// In product usage, this is always set to function newOpencensusSDExporter.
|
||||
// In unit tests this is set to a fake one to avoid calling actual Google API
|
||||
// service.
|
||||
newStackdriverExporterFunc func(stackdriver.Options) (view.Exporter, error)
|
||||
newStackdriverExporterFunc func(sd.Options) (view.Exporter, error)
|
||||
|
||||
// kubeclient is the in-cluster Kubernetes kubeclient, which is lazy-initialized on first use.
|
||||
kubeclient *kubernetes.Clientset
|
||||
|
@ -100,12 +100,15 @@ func init() {
|
|||
kubeclientInitErr = nil
|
||||
}
|
||||
|
||||
func newOpencensusSDExporter(o stackdriver.Options) (view.Exporter, error) {
|
||||
e, err := stackdriver.NewExporter(o)
|
||||
if err == nil {
|
||||
// Start the exporter.
|
||||
// TODO(https://github.com/knative/pkg/issues/866): Move this to an interface.
|
||||
e.StartMetricsExporter()
|
||||
func newOpencensusSDExporter(o sd.Options) (view.Exporter, error) {
|
||||
e, err := sd.NewExporter(o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Start the exporter.
|
||||
// TODO(https://github.com/knative/pkg/issues/866): Move this to an interface.
|
||||
if err := e.StartMetricsExporter(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
@ -120,7 +123,7 @@ func newStackdriverExporter(config *metricsConfig, logger *zap.SugaredLogger) (v
|
|||
logger.Warnw("Issue configuring Stackdriver exporter client options, no additional client options will be used: ", zap.Error(err))
|
||||
}
|
||||
// Automatically fall back on Google application default credentials
|
||||
e, err := newStackdriverExporterFunc(stackdriver.Options{
|
||||
e, err := newStackdriverExporterFunc(sd.Options{
|
||||
ProjectID: gm.project,
|
||||
Location: gm.location,
|
||||
MonitoringClientOptions: co,
|
||||
|
@ -128,7 +131,7 @@ func newStackdriverExporter(config *metricsConfig, logger *zap.SugaredLogger) (v
|
|||
GetMetricPrefix: mpf,
|
||||
ResourceByDescriptor: getResourceByDescriptorFunc(config.stackdriverMetricTypePrefix, gm),
|
||||
ReportingInterval: config.reportingPeriod,
|
||||
DefaultMonitoringLabels: &stackdriver.Labels{},
|
||||
DefaultMonitoringLabels: &sd.Labels{},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Errorw("Failed to create the Stackdriver exporter: ", zap.Error(err))
|
||||
|
|
|
@ -19,7 +19,7 @@ package metrics
|
|||
// InitForTesting initialize the necessary global variables for unit tests.
|
||||
func InitForTesting() {
|
||||
setCurMetricsConfig(&metricsConfig{
|
||||
backendDestination: Prometheus,
|
||||
backendDestination: prometheus,
|
||||
component: "test",
|
||||
domain: "test",
|
||||
})
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
Copyright 2019 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package network holds the typed objects that define the schemas for
|
||||
// configuring the knative networking layer.
|
||||
package network
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
Copyright 2019 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package network
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
resolverFileName = "/etc/resolv.conf"
|
||||
defaultDomainName = "cluster.local"
|
||||
)
|
||||
|
||||
var (
|
||||
domainName string
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// GetServiceHostname returns the fully qualified service hostname
|
||||
func GetServiceHostname(name string, namespace string) string {
|
||||
return fmt.Sprintf("%s.%s.svc.%s", name, namespace, GetClusterDomainName())
|
||||
}
|
||||
|
||||
// GetClusterDomainName returns cluster's domain name or an error
|
||||
// Closes issue: https://github.com/knative/eventing/issues/714
|
||||
func GetClusterDomainName() string {
|
||||
once.Do(func() {
|
||||
f, err := os.Open(resolverFileName)
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
domainName = getClusterDomainName(f)
|
||||
|
||||
} else {
|
||||
domainName = defaultDomainName
|
||||
}
|
||||
})
|
||||
|
||||
return domainName
|
||||
}
|
||||
|
||||
func getClusterDomainName(r io.Reader) string {
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
elements := strings.Split(scanner.Text(), " ")
|
||||
if elements[0] != "search" {
|
||||
continue
|
||||
}
|
||||
for i := 1; i < len(elements)-1; i++ {
|
||||
if strings.HasPrefix(elements[i], "svc.") {
|
||||
return strings.TrimSuffix(elements[i][4:], ".")
|
||||
}
|
||||
}
|
||||
}
|
||||
// For all abnormal cases return default domain name
|
||||
return defaultDomainName
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2019 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package network
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ErrorHandler sets up a handler suitable for use with the ErrorHandler field on
|
||||
// httputil's reverse proxy.
|
||||
func ErrorHandler(logger *zap.SugaredLogger) func(http.ResponseWriter, *http.Request, error) {
|
||||
return func(w http.ResponseWriter, req *http.Request, err error) {
|
||||
ss := readSockStat(logger)
|
||||
logger.Errorw("error reverse proxying request; sockstat: "+ss, zap.Error(err))
|
||||
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||
}
|
||||
}
|
||||
|
||||
func readSockStat(logger *zap.SugaredLogger) string {
|
||||
b, err := ioutil.ReadFile("/proc/net/sockstat")
|
||||
if err != nil {
|
||||
logger.Errorw("Unable to read sockstat", zap.Error(err))
|
||||
return ""
|
||||
}
|
||||
return string(b)
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright 2019 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package network
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
)
|
||||
|
||||
// NewServer returns a new HTTP Server with HTTP2 handler.
|
||||
func NewServer(addr string, h http.Handler) *http.Server {
|
||||
h1s := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: h2c.NewHandler(h, &http2.Server{}),
|
||||
}
|
||||
|
||||
return h1s
|
||||
}
|
||||
|
||||
// NewH2CTransport constructs a new H2C transport.
|
||||
// That transport will reroute all HTTPS traffic to HTTP. This is
|
||||
// to explicitly allow h2c (http2 without TLS) transport.
|
||||
// See https://github.com/golang/go/issues/14141 for more details.
|
||||
func NewH2CTransport() http.RoundTripper {
|
||||
return &http2.Transport{
|
||||
AllowHTTP: true,
|
||||
DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
d := &net.Dialer{
|
||||
Timeout: DefaultConnTimeout,
|
||||
KeepAlive: 5 * time.Second,
|
||||
DualStack: true,
|
||||
}
|
||||
return d.Dial(netw, addr)
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
Copyright 2019 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://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 network
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultConnTimeout specifies a short default connection timeout
|
||||
// to avoid hitting the issue fixed in
|
||||
// https://github.com/kubernetes/kubernetes/pull/72534 but only
|
||||
// avalailable after Kubernetes 1.14.
|
||||
//
|
||||
// Our connections are usually between pods in the same cluster
|
||||
// like activator <-> queue-proxy, or even between containers
|
||||
// within the same pod queue-proxy <-> user-container, so a
|
||||
// smaller connect timeout would be justifiable.
|
||||
//
|
||||
// We should consider exposing this as a configuration.
|
||||
DefaultConnTimeout = 200 * time.Millisecond
|
||||
|
||||
// DefaultDrainTimeout is the time that Knative components on the data
|
||||
// path will wait before shutting down server, but after starting to fail
|
||||
// readiness probes to ensure network layer propagation and so that no requests
|
||||
// are routed to this pod.
|
||||
DefaultDrainTimeout = 30 * time.Second
|
||||
|
||||
// UserAgentKey is the constant for header "User-Agent".
|
||||
UserAgentKey = "User-Agent"
|
||||
|
||||
// ProbeHeaderName is the name of a header that can be added to
|
||||
// requests to probe the knative networking layer. Requests
|
||||
// with this header will not be passed to the user container or
|
||||
// included in request metrics.
|
||||
ProbeHeaderName = "K-Network-Probe"
|
||||
|
||||
// Since K8s 1.8, prober requests have
|
||||
// User-Agent = "kube-probe/{major-version}.{minor-version}".
|
||||
KubeProbeUAPrefix = "kube-probe/"
|
||||
|
||||
// Istio with mTLS rewrites probes, but their probes pass a different
|
||||
// user-agent. So we augment the probes with this header.
|
||||
KubeletProbeHeaderName = "K-Kubelet-Probe"
|
||||
)
|
||||
|
||||
// IsKubeletProbe returns true if the request is a Kubernetes probe.
|
||||
func IsKubeletProbe(r *http.Request) bool {
|
||||
return strings.HasPrefix(r.Header.Get("User-Agent"), KubeProbeUAPrefix) ||
|
||||
r.Header.Get(KubeletProbeHeaderName) != ""
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
Copyright 2019 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
// RoundTripperFunc implementation roundtrips a request.
|
||||
type RoundTripperFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
// RoundTrip implements http.RoundTripper.
|
||||
func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
return rt(r)
|
||||
}
|
||||
|
||||
func newAutoTransport(v1 http.RoundTripper, v2 http.RoundTripper) http.RoundTripper {
|
||||
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
|
||||
t := v1
|
||||
if r.ProtoMajor == 2 {
|
||||
t = v2
|
||||
}
|
||||
return t.RoundTrip(r)
|
||||
})
|
||||
}
|
||||
|
||||
const sleepTO = 30 * time.Millisecond
|
||||
|
||||
var backOffTemplate = wait.Backoff{
|
||||
Duration: 50 * time.Millisecond,
|
||||
Factor: 1.4,
|
||||
Jitter: 0.1, // At most 10% jitter.
|
||||
Steps: 15,
|
||||
}
|
||||
|
||||
// DialWithBackOff executes `net.Dialer.DialContext()` with exponentially increasing
|
||||
// dial timeouts. In addition it sleeps with random jitter between tries.
|
||||
var DialWithBackOff = NewBackoffDialer(backOffTemplate)
|
||||
|
||||
// NewBackoffDialer returns a dialer that executes `net.Dialer.DialContext()` with
|
||||
// exponentially increasing dial timeouts. In addition it sleeps with random jitter
|
||||
// between tries.
|
||||
func NewBackoffDialer(backoffConfig wait.Backoff) func(context.Context, string, string) (net.Conn, error) {
|
||||
return func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return dialBackOffHelper(ctx, network, address, backoffConfig, sleepTO)
|
||||
}
|
||||
}
|
||||
|
||||
func dialBackOffHelper(ctx context.Context, network, address string, bo wait.Backoff, sleep time.Duration) (net.Conn, error) {
|
||||
dialer := &net.Dialer{
|
||||
Timeout: bo.Duration, // Initial duration.
|
||||
KeepAlive: 5 * time.Second,
|
||||
DualStack: true,
|
||||
}
|
||||
start := time.Now()
|
||||
for {
|
||||
c, err := dialer.DialContext(ctx, network, address)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
if bo.Steps < 1 {
|
||||
break
|
||||
}
|
||||
dialer.Timeout = bo.Step()
|
||||
time.Sleep(wait.Jitter(sleep, 1.0)) // Sleep with jitter.
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
return nil, fmt.Errorf("timed out dialing after %.2fs", elapsed.Seconds())
|
||||
}
|
||||
|
||||
func newHTTPTransport(connTimeout time.Duration, disableKeepAlives bool) http.RoundTripper {
|
||||
return &http.Transport{
|
||||
// Those match net/http/transport.go
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
DisableKeepAlives: disableKeepAlives,
|
||||
|
||||
// Those are bespoke.
|
||||
DialContext: DialWithBackOff,
|
||||
MaxIdleConns: 1000,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
}
|
||||
}
|
||||
|
||||
// NewProberTransport creates a RoundTripper that is useful for probing,
|
||||
// since it will not cache connections.
|
||||
func NewProberTransport() http.RoundTripper {
|
||||
return newAutoTransport(
|
||||
newHTTPTransport(DefaultConnTimeout, true /*disable keep-alives*/),
|
||||
NewH2CTransport())
|
||||
}
|
||||
|
||||
// NewAutoTransport creates a RoundTripper that can use appropriate transport
|
||||
// based on the request's HTTP version.
|
||||
func NewAutoTransport() http.RoundTripper {
|
||||
return newAutoTransport(
|
||||
newHTTPTransport(DefaultConnTimeout, false /*disable keep-alives*/),
|
||||
NewH2CTransport())
|
||||
}
|
||||
|
||||
// AutoTransport uses h2c for HTTP2 requests and falls back to `http.DefaultTransport` for all others
|
||||
var AutoTransport = NewAutoTransport()
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright 2020 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package reconciler
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// Bucket is an opaque type used to scope leadership.
|
||||
type Bucket interface {
|
||||
// Name returns a string representing this bucket, which uniquely
|
||||
// identifies the bucket and is suitable for use as a resource lock name.
|
||||
Name() string
|
||||
|
||||
// Has determines whether this Bucket contains a particular key.
|
||||
Has(key types.NamespacedName) bool
|
||||
}
|
||||
|
||||
// UniversalBucket returns a Bucket that "Has()" all keys.
|
||||
func UniversalBucket() Bucket {
|
||||
return &bucket{}
|
||||
}
|
||||
|
||||
type bucket struct{}
|
||||
|
||||
var _ Bucket = (*bucket)(nil)
|
||||
|
||||
// Name implements Bucket
|
||||
func (b *bucket) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Has implements Bucket
|
||||
func (b *bucket) Has(nn types.NamespacedName) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// LeaderAware is implemented by Reconcilers that are aware of their leader status.
|
||||
type LeaderAware interface {
|
||||
// Promote is called when we become the leader of a given Bucket. It must be
|
||||
// supplied with an enqueue function through which a Bucket resync may be triggered.
|
||||
Promote(b Bucket, enq func(Bucket, types.NamespacedName)) error
|
||||
|
||||
// Demote is called when we stop being the leader for the specified Bucket.
|
||||
Demote(Bucket)
|
||||
}
|
||||
|
||||
// LeaderAwareFuncs implements LeaderAware using the given functions for handling
|
||||
// promotion and demotion.
|
||||
type LeaderAwareFuncs struct {
|
||||
sync.RWMutex
|
||||
buckets map[string]Bucket
|
||||
|
||||
PromoteFunc func(b Bucket, enq func(Bucket, types.NamespacedName)) error
|
||||
DemoteFunc func(b Bucket)
|
||||
}
|
||||
|
||||
var _ LeaderAware = (*LeaderAwareFuncs)(nil)
|
||||
|
||||
// IsLeaderFor implements LeaderAware
|
||||
func (laf *LeaderAwareFuncs) IsLeaderFor(key types.NamespacedName) bool {
|
||||
laf.RLock()
|
||||
defer laf.RUnlock()
|
||||
|
||||
for _, bkt := range laf.buckets {
|
||||
if bkt.Has(key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Promote implements LeaderAware
|
||||
func (laf *LeaderAwareFuncs) Promote(b Bucket, enq func(Bucket, types.NamespacedName)) error {
|
||||
func() {
|
||||
laf.Lock()
|
||||
defer laf.Unlock()
|
||||
if laf.buckets == nil {
|
||||
laf.buckets = make(map[string]Bucket, 1)
|
||||
}
|
||||
laf.buckets[b.Name()] = b
|
||||
}()
|
||||
|
||||
if promote := laf.PromoteFunc; promote != nil {
|
||||
return promote(b, enq)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Demote implements LeaderAware
|
||||
func (laf *LeaderAwareFuncs) Demote(b Bucket) {
|
||||
func() {
|
||||
laf.Lock()
|
||||
defer laf.Unlock()
|
||||
delete(laf.buckets, b.Name())
|
||||
}()
|
||||
|
||||
if demote := laf.DemoteFunc; demote != nil {
|
||||
demote(b)
|
||||
}
|
||||
}
|
|
@ -18,7 +18,12 @@ package reconciler
|
|||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"knative.dev/pkg/apis"
|
||||
duckv1 "knative.dev/pkg/apis/duck/v1"
|
||||
"knative.dev/pkg/logging"
|
||||
)
|
||||
|
@ -29,28 +34,60 @@ const failedGenerationBump = "NewObservedGenFailure"
|
|||
func PreProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
|
||||
newStatus := resource.GetStatus()
|
||||
|
||||
if newStatus.ObservedGeneration != resource.GetGeneration() {
|
||||
condSet := resource.GetConditionSet()
|
||||
manager := condSet.Manage(newStatus)
|
||||
// We may be reading a version of the object that was stored at an older version
|
||||
// and may not have had all of the assumed defaults specified. This won't result
|
||||
// in this getting written back to the API Server, but lets downstream logic make
|
||||
// assumptions about defaulting.
|
||||
if d, ok := resource.(apis.Defaultable); ok {
|
||||
d.SetDefaults(ctx)
|
||||
}
|
||||
|
||||
// Ensure conditions are initialized before we modify.
|
||||
condSet := resource.GetConditionSet()
|
||||
manager := condSet.Manage(newStatus)
|
||||
manager.InitializeConditions()
|
||||
|
||||
if newStatus.ObservedGeneration != resource.GetGeneration() {
|
||||
// Reset Ready/Successful to unknown. The reconciler is expected to overwrite this.
|
||||
manager.MarkUnknown(condSet.GetTopLevelConditionType(), failedGenerationBump, "unsuccessfully observed a new generation")
|
||||
}
|
||||
}
|
||||
|
||||
// PostProcessReconcile contains logic to apply after reconciliation of a resource.
|
||||
func PostProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
|
||||
func PostProcessReconcile(ctx context.Context, resource, oldResource duckv1.KRShaped) {
|
||||
logger := logging.FromContext(ctx)
|
||||
newStatus := resource.GetStatus()
|
||||
mgr := resource.GetConditionSet().Manage(newStatus)
|
||||
status := resource.GetStatus()
|
||||
mgr := resource.GetConditionSet().Manage(status)
|
||||
|
||||
// Bump observed generation to denote that we have processed this
|
||||
// generation regardless of success or failure.
|
||||
newStatus.ObservedGeneration = resource.GetGeneration()
|
||||
status.ObservedGeneration = resource.GetGeneration()
|
||||
|
||||
if rc := mgr.GetTopLevelCondition(); rc == nil {
|
||||
logger.Warn("A reconciliation included no top-level condition")
|
||||
} else if rc.Reason == failedGenerationBump {
|
||||
logger.Warn("A reconciler observed a new generation without updating the resource status")
|
||||
}
|
||||
|
||||
groomConditionsTransitionTime(resource, oldResource)
|
||||
}
|
||||
|
||||
// groomConditionsTransitionTime ensures that the LastTransitionTime only advances for resources
|
||||
// where the condition has changed during reconciliation. This also ensures that all advanced
|
||||
// conditions share the same timestamp.
|
||||
func groomConditionsTransitionTime(resource, oldResource duckv1.KRShaped) {
|
||||
now := apis.VolatileTime{Inner: metav1.NewTime(time.Now())}
|
||||
sts := resource.GetStatus()
|
||||
for i := range sts.Conditions {
|
||||
cond := &sts.Conditions[i]
|
||||
|
||||
if oldCond := oldResource.GetStatus().GetCondition(cond.Type); oldCond != nil {
|
||||
cond.LastTransitionTime = oldCond.LastTransitionTime
|
||||
if reflect.DeepEqual(cond, oldCond) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
cond.LastTransitionTime = now
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright 2019 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Mockable interface for time based testing
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
type RealClock struct{}
|
||||
|
||||
func (RealClock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
Copyright 2019 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
NamespaceEnvKey = "SYSTEM_NAMESPACE"
|
||||
ResourceLabelEnvKey = "SYSTEM_RESOURCE_LABEL"
|
||||
)
|
||||
|
||||
// Namespace returns the name of the K8s namespace where our system components
|
||||
// run.
|
||||
func Namespace() string {
|
||||
if ns := os.Getenv(NamespaceEnvKey); ns != "" {
|
||||
return ns
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf(`The environment variable %q is not set
|
||||
|
||||
If this is a process running on Kubernetes, then it should be using the downward
|
||||
API to initialize this variable via:
|
||||
|
||||
env:
|
||||
- name: %s
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
|
||||
If this is a Go unit test consuming system.Namespace() then it should add the
|
||||
following import:
|
||||
|
||||
import (
|
||||
_ "knative.dev/pkg/system/testing"
|
||||
)`, NamespaceEnvKey, NamespaceEnvKey))
|
||||
}
|
||||
|
||||
// ResourceLabel returns the label key identifying K8s objects our system
|
||||
// components source their configuration from.
|
||||
func ResourceLabel() string {
|
||||
return os.Getenv(ResourceLabelEnvKey)
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
/*
|
||||
Copyright 2018 The Knative Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://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.
|
||||
|
@ -11,13 +14,6 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package scripts is a placeholder that allows us to pull the shell scripts
|
||||
// via go mod vendor.
|
||||
package scripts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("This is a dummy go file so `go dep` can be used with knative/test-infra/scripts")
|
||||
fmt.Println("This file can be safely removed if one day this directory contains real, useful go code")
|
||||
}
|
||||
|
|
|
@ -108,6 +108,8 @@ function dump_cluster_state() {
|
|||
echo ">>> Details" >> ${output}
|
||||
if [[ "${crd}" == "secrets" ]]; then
|
||||
echo "Secrets are ignored for security reasons" >> ${output}
|
||||
elif [[ "${crd}" == "events" ]]; then
|
||||
echo "events are ignored as making a lot of noise" >> ${output}
|
||||
else
|
||||
kubectl get ${crd} --all-namespaces -o yaml >> ${output}
|
||||
fi
|
||||
|
|
|
@ -389,25 +389,8 @@ function mktemp_with_extension() {
|
|||
# $2 - check name as an identifier (e.g., GoBuild)
|
||||
# $3 - failure message (can contain newlines), optional (means success)
|
||||
function create_junit_xml() {
|
||||
local xml="$(mktemp_with_extension ${ARTIFACTS}/junit_XXXXXXXX xml)"
|
||||
local failure=""
|
||||
if [[ "$3" != "" ]]; then
|
||||
# Transform newlines into HTML code.
|
||||
# Also escape `<` and `>` as here: https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/encoding/json/encode.go#L48,
|
||||
# this is temporary solution for fixing https://github.com/knative/test-infra/issues/1204,
|
||||
# which should be obsolete once Test-infra 2.0 is in place
|
||||
local msg="$(echo -n "$3" | sed 's/$/\
/g' | sed 's/</\\u003c/' | sed 's/>/\\u003e/' | sed 's/&/\\u0026/' | tr -d '\n')"
|
||||
failure="<failure message=\"Failed\" type=\"\">${msg}</failure>"
|
||||
fi
|
||||
cat << EOF > "${xml}"
|
||||
<testsuites>
|
||||
<testsuite tests="1" failures="1" time="0.000" name="$1">
|
||||
<testcase classname="" name="$2" time="0.0">
|
||||
${failure}
|
||||
</testcase>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
EOF
|
||||
local xml="$(mktemp_with_extension "${ARTIFACTS}"/junit_XXXXXXXX xml)"
|
||||
run_kntest junit --suite="$1" --name="$2" --err-msg="$3" --dest="${xml}" || return 1
|
||||
}
|
||||
|
||||
# Runs a go test and generate a junit summary.
|
||||
|
@ -449,14 +432,18 @@ function report_go_test() {
|
|||
}
|
||||
|
||||
# Install Knative Serving in the current cluster.
|
||||
# Parameters: $1 - Knative Serving manifest.
|
||||
# Parameters: $1 - Knative Serving crds manifest.
|
||||
# $2 - Knative Serving core manifest.
|
||||
# $3 - Knative net-istio manifest.
|
||||
function start_knative_serving() {
|
||||
header "Starting Knative Serving"
|
||||
subheader "Installing Knative Serving"
|
||||
echo "Installing Serving CRDs from $1"
|
||||
kubectl apply --selector knative.dev/crd-install=true -f "$1"
|
||||
echo "Installing the rest of serving components from $1"
|
||||
kubectl apply -f "$1"
|
||||
echo "Installing Serving core components from $2"
|
||||
kubectl apply -f "$2"
|
||||
echo "Installing net-istio components from $3"
|
||||
kubectl apply -f "$3"
|
||||
wait_until_pods_running knative-serving || return 1
|
||||
}
|
||||
|
||||
|
@ -478,12 +465,14 @@ function start_knative_monitoring() {
|
|||
# Install the stable release Knative/serving in the current cluster.
|
||||
# Parameters: $1 - Knative Serving version number, e.g. 0.6.0.
|
||||
function start_release_knative_serving() {
|
||||
start_knative_serving "https://storage.googleapis.com/knative-releases/serving/previous/v$1/serving.yaml"
|
||||
start_knative_serving "https://storage.googleapis.com/knative-releases/serving/previous/v$1/serving-crds.yaml" \
|
||||
"https://storage.googleapis.com/knative-releases/serving/previous/v$1/serving-core.yaml" \
|
||||
"https://storage.googleapis.com/knative-releases/net-istio/previous/v$1/net-istio.yaml"
|
||||
}
|
||||
|
||||
# Install the latest stable Knative Serving in the current cluster.
|
||||
function start_latest_knative_serving() {
|
||||
start_knative_serving "${KNATIVE_SERVING_RELEASE}"
|
||||
start_knative_serving "${KNATIVE_SERVING_RELEASE_CRDS}" "${KNATIVE_SERVING_RELEASE_CORE}" "${KNATIVE_NET_ISTIO_RELEASE}"
|
||||
}
|
||||
|
||||
# Install Knative Eventing in the current cluster.
|
||||
|
@ -536,6 +525,24 @@ function run_go_tool() {
|
|||
${tool} "$@"
|
||||
}
|
||||
|
||||
# Run kntest tool, error out and ask users to install it if it's not currently installed.
|
||||
# Parameters: $1..$n - parameters passed to the tool.
|
||||
function run_kntest() {
|
||||
# If the current repo is test-infra, run kntest from source.
|
||||
if [[ "${REPO_NAME}" == "test-infra" ]]; then
|
||||
# Each parameter can possibly be in the format of "--xxx yyy", using $@ can automatically split them into multiple positional arguments for the command.
|
||||
# shellcheck disable=SC2068
|
||||
go run "${REPO_ROOT_DIR}"/kntest/cmd/kntest $@
|
||||
# Otherwise kntest must be installed.
|
||||
else
|
||||
if [[ ! -x "$(command -v kntest)" ]]; then
|
||||
echo "--- FAIL: kntest not installed, please clone test-infra repo and run \`go install ./kntest/cmd/kntest\` to install it"; return 1;
|
||||
fi
|
||||
# shellcheck disable=SC2068
|
||||
kntest $@
|
||||
fi
|
||||
}
|
||||
|
||||
# Run go-licenses to update licenses.
|
||||
# Parameters: $1 - output file, relative to repo root dir.
|
||||
# $2 - directory to inspect.
|
||||
|
@ -742,6 +749,8 @@ readonly _TEST_INFRA_SCRIPTS_DIR="$(dirname $(get_canonical_path ${BASH_SOURCE[0
|
|||
readonly REPO_NAME_FORMATTED="Knative $(capitalize ${REPO_NAME//-/ })"
|
||||
|
||||
# Public latest nightly or release yaml files.
|
||||
readonly KNATIVE_SERVING_RELEASE="$(get_latest_knative_yaml_source "serving" "serving")"
|
||||
readonly KNATIVE_SERVING_RELEASE_CRDS="$(get_latest_knative_yaml_source "serving" "serving-crds")"
|
||||
readonly KNATIVE_SERVING_RELEASE_CORE="$(get_latest_knative_yaml_source "serving" "serving-core")"
|
||||
readonly KNATIVE_NET_ISTIO_RELEASE="$(get_latest_knative_yaml_source "net-istio" "net-istio")"
|
||||
readonly KNATIVE_EVENTING_RELEASE="$(get_latest_knative_yaml_source "eventing" "eventing")"
|
||||
readonly KNATIVE_MONITORING_RELEASE="$(get_latest_knative_yaml_source "serving" "monitoring")"
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 The Knative Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e
|
||||
|
||||
# This script updates test-infra scripts in-repo.
|
||||
# Run it to update (usually from hack/update-deps.sh) the current scripts.
|
||||
# Scripts are installed to REPO_ROOT/scripts/test-infra
|
||||
|
||||
# The following arguments are accepted:
|
||||
# --update
|
||||
# Do the update
|
||||
# --ref X
|
||||
# Defines which ref (branch, tag, commit) of test-infra to get scripts from; defaults to master
|
||||
# --first-time
|
||||
# Run this script from your repo root directory to install scripts for the first time
|
||||
# Will also sed -i non-vendor scripts in the current repo to point to new path
|
||||
# TODO: --verify
|
||||
# Verify the contents of scripts/test-infra match the contents from commit sha in scripts/test-infra/COMMIT
|
||||
# One can verify manually by running the script with '--ref $(cat scripts/test-infra/COMMIT)' and ensuring no files are staged
|
||||
|
||||
declare -i FIRST_TIME_SETUP=0
|
||||
declare -i DO_UPDATE=0
|
||||
declare SCRIPTS_REF=master
|
||||
|
||||
while [[ $# -ne 0 ]]; do
|
||||
parameter="$1"
|
||||
case ${parameter} in
|
||||
--ref)
|
||||
shift
|
||||
SCRIPTS_REF="$1"
|
||||
;;
|
||||
--first-time)
|
||||
FIRST_TIME_SETUP=1
|
||||
;;
|
||||
--update)
|
||||
DO_UPDATE=1
|
||||
;;
|
||||
*)
|
||||
echo "unknown option ${parameter}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
function do_read_tree() {
|
||||
mkdir -p scripts/test-infra
|
||||
git read-tree --prefix=scripts/test-infra -u "test-infra/${SCRIPTS_REF}:scripts"
|
||||
git show-ref -s -- "refs/remotes/test-infra/${SCRIPTS_REF}" > scripts/test-infra/COMMIT
|
||||
git add scripts/test-infra/COMMIT
|
||||
echo "test-infra scripts installed to scripts/test-infra from branch ${SCRIPTS_REF}"
|
||||
}
|
||||
|
||||
function run() {
|
||||
if (( FIRST_TIME_SETUP )); then
|
||||
if [[ ! -d .git ]]; then
|
||||
echo "I don't believe you are in a repo root; exiting"
|
||||
exit 5
|
||||
fi
|
||||
git remote add test-infra https://github.com/knative/test-infra.git || echo "test-infra remote already set; not changing"
|
||||
git fetch test-infra "${SCRIPTS_REF}"
|
||||
do_read_tree
|
||||
echo "Attempting to point all scripts to use this new path"
|
||||
grep -RiIl vendor/knative.dev/test-infra | grep -v ^vendor | grep -v ^scripts/test-infra | xargs sed -i 's+vendor/knative.dev/test-infra/scripts+scripts/test-infra+'
|
||||
elif (( DO_UPDATE )); then
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../.."
|
||||
trap popd EXIT
|
||||
|
||||
git remote add test-infra https://github.com/knative/test-infra.git || true
|
||||
git fetch test-infra "${SCRIPTS_REF}"
|
||||
git rm -fr scripts/test-infra
|
||||
rm -fR scripts/test-infra
|
||||
do_read_tree
|
||||
fi
|
||||
}
|
||||
|
||||
run
|
|
@ -14,7 +14,7 @@ contrib.go.opencensus.io/exporter/stackdriver/monitoredresource
|
|||
github.com/PuerkitoBio/purell
|
||||
# github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578
|
||||
github.com/PuerkitoBio/urlesc
|
||||
# github.com/aws/aws-sdk-go v1.29.34
|
||||
# github.com/aws/aws-sdk-go v1.30.5
|
||||
github.com/aws/aws-sdk-go/aws
|
||||
github.com/aws/aws-sdk-go/aws/awserr
|
||||
github.com/aws/aws-sdk-go/aws/awsutil
|
||||
|
@ -137,6 +137,8 @@ github.com/influxdata/tdigest
|
|||
github.com/jmespath/go-jmespath
|
||||
# github.com/json-iterator/go v1.1.9
|
||||
github.com/json-iterator/go
|
||||
# github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/kelseyhightower/envconfig
|
||||
# github.com/mailru/easyjson v0.7.0
|
||||
github.com/mailru/easyjson/buffer
|
||||
github.com/mailru/easyjson/jlexer
|
||||
|
@ -180,7 +182,7 @@ github.com/tsenart/vegeta/internal/resolver
|
|||
github.com/tsenart/vegeta/lib
|
||||
github.com/tsenart/vegeta/lib/lttb
|
||||
github.com/tsenart/vegeta/lib/plot
|
||||
# go.opencensus.io v0.22.3
|
||||
# go.opencensus.io v0.22.4
|
||||
go.opencensus.io
|
||||
go.opencensus.io/internal
|
||||
go.opencensus.io/internal/tagencoding
|
||||
|
@ -221,6 +223,7 @@ golang.org/x/net/context
|
|||
golang.org/x/net/context/ctxhttp
|
||||
golang.org/x/net/http/httpguts
|
||||
golang.org/x/net/http2
|
||||
golang.org/x/net/http2/h2c
|
||||
golang.org/x/net/http2/hpack
|
||||
golang.org/x/net/idna
|
||||
golang.org/x/net/internal/timeseries
|
||||
|
@ -254,7 +257,7 @@ golang.org/x/tools/internal/imports
|
|||
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
golang.org/x/xerrors
|
||||
golang.org/x/xerrors/internal
|
||||
# gomodules.xyz/jsonpatch/v2 v2.0.1
|
||||
# gomodules.xyz/jsonpatch/v2 v2.1.0
|
||||
gomodules.xyz/jsonpatch/v2
|
||||
# gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485
|
||||
gonum.org/v1/gonum/blas
|
||||
|
@ -449,6 +452,7 @@ k8s.io/apimachinery/pkg/util/net
|
|||
k8s.io/apimachinery/pkg/util/runtime
|
||||
k8s.io/apimachinery/pkg/util/sets
|
||||
k8s.io/apimachinery/pkg/util/strategicpatch
|
||||
k8s.io/apimachinery/pkg/util/uuid
|
||||
k8s.io/apimachinery/pkg/util/validation
|
||||
k8s.io/apimachinery/pkg/util/validation/field
|
||||
k8s.io/apimachinery/pkg/util/wait
|
||||
|
@ -607,6 +611,8 @@ k8s.io/client-go/rest/watch
|
|||
k8s.io/client-go/testing
|
||||
k8s.io/client-go/tools/cache
|
||||
k8s.io/client-go/tools/clientcmd/api
|
||||
k8s.io/client-go/tools/leaderelection
|
||||
k8s.io/client-go/tools/leaderelection/resourcelock
|
||||
k8s.io/client-go/tools/metrics
|
||||
k8s.io/client-go/tools/pager
|
||||
k8s.io/client-go/tools/record
|
||||
|
@ -680,7 +686,7 @@ k8s.io/kube-openapi/pkg/util/sets
|
|||
k8s.io/utils/buffer
|
||||
k8s.io/utils/integer
|
||||
k8s.io/utils/trace
|
||||
# knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7
|
||||
# knative.dev/pkg v0.0.0-20200630170034-2c1a029eb97f
|
||||
## explicit
|
||||
knative.dev/pkg/apis
|
||||
knative.dev/pkg/apis/duck
|
||||
|
@ -695,17 +701,20 @@ knative.dev/pkg/hack
|
|||
knative.dev/pkg/injection
|
||||
knative.dev/pkg/kmeta
|
||||
knative.dev/pkg/kmp
|
||||
knative.dev/pkg/leaderelection
|
||||
knative.dev/pkg/logging
|
||||
knative.dev/pkg/logging/logkey
|
||||
knative.dev/pkg/metrics
|
||||
knative.dev/pkg/metrics/metricskey
|
||||
knative.dev/pkg/network
|
||||
knative.dev/pkg/reconciler
|
||||
knative.dev/pkg/system
|
||||
knative.dev/pkg/tracker
|
||||
# knative.dev/test-infra v0.0.0-20200519161858-554a95a37986
|
||||
# knative.dev/test-infra v0.0.0-20200630141629-15f40fe97047
|
||||
## explicit
|
||||
knative.dev/test-infra/scripts
|
||||
knative.dev/test-infra/tools/dep-collector
|
||||
# sigs.k8s.io/yaml v1.1.0
|
||||
# sigs.k8s.io/yaml v1.2.0
|
||||
sigs.k8s.io/yaml
|
||||
# k8s.io/api => k8s.io/api v0.17.6
|
||||
# k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.17.6
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
language: go
|
||||
dist: xenial
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d .)
|
||||
- diff -u <(echo -n) <(gofmt -d *.go)
|
||||
- diff -u <(echo -n) <(golint $(go list -e ./...) | grep -v YAMLToJSON)
|
||||
- go tool vet .
|
||||
- go test -v -race ./...
|
||||
- GO111MODULE=on go vet .
|
||||
- GO111MODULE=on go test -v -race ./...
|
||||
- git diff --exit-code
|
||||
install:
|
||||
- go get golang.org/x/lint/golint
|
||||
- GO111MODULE=off go get golang.org/x/lint/golint
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
# YAML marshaling and unmarshaling support for Go
|
||||
|
||||
[](https://travis-ci.org/ghodss/yaml)
|
||||
[](https://travis-ci.org/kubernetes-sigs/yaml)
|
||||
|
||||
kubernetes-sigs/yaml is a permanent fork of [ghodss/yaml](https://github.com/ghodss/yaml).
|
||||
|
||||
## Introduction
|
||||
|
||||
A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs.
|
||||
|
||||
In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/).
|
||||
In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://web.archive.org/web/20190603050330/http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/).
|
||||
|
||||
## Compatibility
|
||||
|
||||
|
@ -32,13 +34,13 @@ GOOD:
|
|||
To install, run:
|
||||
|
||||
```
|
||||
$ go get github.com/ghodss/yaml
|
||||
$ go get sigs.k8s.io/yaml
|
||||
```
|
||||
|
||||
And import using:
|
||||
|
||||
```
|
||||
import "github.com/ghodss/yaml"
|
||||
import "sigs.k8s.io/yaml"
|
||||
```
|
||||
|
||||
Usage is very similar to the JSON library:
|
||||
|
@ -49,7 +51,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type Person struct {
|
||||
|
@ -93,7 +95,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
module sigs.k8s.io/yaml
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
)
|
|
@ -0,0 +1,9 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -317,3 +317,64 @@ func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (in
|
|||
return yamlObj, nil
|
||||
}
|
||||
}
|
||||
|
||||
// JSONObjectToYAMLObject converts an in-memory JSON object into a YAML in-memory MapSlice,
|
||||
// without going through a byte representation. A nil or empty map[string]interface{} input is
|
||||
// converted to an empty map, i.e. yaml.MapSlice(nil).
|
||||
//
|
||||
// interface{} slices stay interface{} slices. map[string]interface{} becomes yaml.MapSlice.
|
||||
//
|
||||
// int64 and float64 are down casted following the logic of github.com/go-yaml/yaml:
|
||||
// - float64s are down-casted as far as possible without data-loss to int, int64, uint64.
|
||||
// - int64s are down-casted to int if possible without data-loss.
|
||||
//
|
||||
// Big int/int64/uint64 do not lose precision as in the json-yaml roundtripping case.
|
||||
//
|
||||
// string, bool and any other types are unchanged.
|
||||
func JSONObjectToYAMLObject(j map[string]interface{}) yaml.MapSlice {
|
||||
if len(j) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make(yaml.MapSlice, 0, len(j))
|
||||
for k, v := range j {
|
||||
ret = append(ret, yaml.MapItem{Key: k, Value: jsonToYAMLValue(v)})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func jsonToYAMLValue(j interface{}) interface{} {
|
||||
switch j := j.(type) {
|
||||
case map[string]interface{}:
|
||||
if j == nil {
|
||||
return interface{}(nil)
|
||||
}
|
||||
return JSONObjectToYAMLObject(j)
|
||||
case []interface{}:
|
||||
if j == nil {
|
||||
return interface{}(nil)
|
||||
}
|
||||
ret := make([]interface{}, len(j))
|
||||
for i := range j {
|
||||
ret[i] = jsonToYAMLValue(j[i])
|
||||
}
|
||||
return ret
|
||||
case float64:
|
||||
// replicate the logic in https://github.com/go-yaml/yaml/blob/51d6538a90f86fe93ac480b35f37b2be17fef232/resolve.go#L151
|
||||
if i64 := int64(j); j == float64(i64) {
|
||||
if i := int(i64); i64 == int64(i) {
|
||||
return i
|
||||
}
|
||||
return i64
|
||||
}
|
||||
if ui64 := uint64(j); j == float64(ui64) {
|
||||
return ui64
|
||||
}
|
||||
return j
|
||||
case int64:
|
||||
if i := int(j); j == int64(i) {
|
||||
return i
|
||||
}
|
||||
return j
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue