Merge branch 'master' into bitbucket-support

This commit is contained in:
runzexia
2019-08-27 16:12:23 +08:00
680 changed files with 120037 additions and 369 deletions

4
.gitattributes vendored
View File

@@ -1,2 +1,6 @@
pkg/cmd/api/spec/api.swagger.json linguist-generated=true
pkg/cmd/api/spec/static.go linguist-generated=true
pkg/client/* linguist-generated=true
config/crds/* linguist-generated=true
config/rbac/* linguist-generated=true
zz_generated.deepcopy.go linguist-generated=true

2
.gitignore vendored
View File

@@ -28,3 +28,5 @@ apiserver.local.config
.DS_Store
api.json
*.coverprofile
kustomize/network/etcd

View File

@@ -94,7 +94,7 @@ clean:
# find or download controller-gen
# download controller-gen if necessary
clientset: generate
clientset:
./hack/generate_client.sh
@@ -106,7 +106,7 @@ internal-crds:
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./pkg/apis/network/..." output:crd:artifacts:config=config/crd/bases
internal-generate-apis: internal-controller-gen
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/...
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/network/...
internal-controller-gen:
ifeq (, $(shell which controller-gen))

View File

@@ -1,3 +1,4 @@
API rule violation: names_match,./devops/v1alpha1,S2iBinarySpec,MD5
API rule violation: names_match,k8s.io/api/core/v1,AzureDiskVolumeSource,DataDiskURI
API rule violation: names_match,k8s.io/api/core/v1,ContainerStatus,LastTerminationState
API rule violation: names_match,k8s.io/api/core/v1,DaemonEndpoint,Port
@@ -44,4 +45,3 @@ API rule violation: names_match,k8s.io/apimachinery/pkg/runtime,Unknown,Raw
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,IntVal
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,StrVal
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,Type
API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1,S2iBinarySpec,MD5

View File

@@ -0,0 +1,4 @@
FROM gcr.io/distroless/static:latest
WORKDIR /
COPY ks-network .
ENTRYPOINT ["/ks-network"]

24
cmd/ks-network/main.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import (
"flag"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/controller/network/runoption"
)
var opt runoption.RunOption
func init() {
flag.StringVar(&opt.ProviderName, "np-provider", "calico", "specify the network policy provider, k8s or calico")
flag.BoolVar(&opt.AllowInsecureEtcd, "allow-insecure-etcd", false, "specify allow connect to etcd using insecure http")
//TODO add more flags
}
func main() {
klog.InitFlags(nil)
flag.Set("logtostderr", "true")
flag.Parse()
klog.V(1).Info("Preparing kubernetes client")
klog.Fatal(opt.Run())
}

View File

@@ -0,0 +1,9 @@
apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
labels:
controller-tools.k8s.io: "1.0"
name: namespacenetworkpolicy-sample
spec:
# Add fields here
foo: bar

View File

29
go.mod
View File

@@ -5,6 +5,7 @@ go 1.12
require (
bitbucket.org/ww/goautoneg v0.0.0-20120707110453-75cd24fc2f2c // indirect
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Azure/go-autorest/autorest v0.5.0 // indirect
github.com/Microsoft/go-winio v0.4.12 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/PuerkitoBio/goquery v1.5.0
@@ -36,9 +37,14 @@ require (
github.com/evanphx/json-patch v4.2.0+incompatible // indirect
github.com/fatih/structs v1.1.0
github.com/go-ldap/ldap v3.0.3+incompatible
github.com/go-logr/logr v0.1.0 // indirect
github.com/go-logr/logr v0.1.0
github.com/go-logr/zapr v0.1.1 // indirect
github.com/go-openapi/spec v0.19.2
github.com/go-openapi/jsonpointer v0.19.0 // indirect
github.com/go-openapi/jsonreference v0.19.0 // indirect
github.com/go-openapi/spec v0.19.0
github.com/go-openapi/swag v0.19.0 // indirect
github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/go-redis/redis v6.15.2+incompatible
github.com/go-sql-driver/mysql v1.4.1
github.com/gobuffalo/flect v0.1.5 // indirect
@@ -46,12 +52,12 @@ require (
github.com/golang/example v0.0.0-20170904185048-46695d81d1fa
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
github.com/golang/protobuf v1.3.1 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/gofuzz v1.0.0 // indirect
github.com/google/uuid v1.1.1
github.com/googleapis/gnostic v0.2.0 // indirect
github.com/gophercloud/gophercloud v0.3.0 // indirect
github.com/gorilla/mux v1.7.1 // indirect
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
@@ -64,6 +70,7 @@ require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/json-iterator/go v1.1.6
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/kiali/kiali v1.1.0
github.com/klauspost/cpuid v1.2.1 // indirect
github.com/knative/pkg v0.0.0-20190314204845-cd278f2d3394
@@ -71,6 +78,7 @@ require (
github.com/kubernetes-sigs/application v0.0.0-20190404151855-67ae7f915d4e
github.com/kubesphere/s2ioperator v0.0.12
github.com/kubesphere/sonargo v0.0.2
github.com/leodido/go-urn v1.1.0 // indirect
github.com/lib/pq v1.2.0 // indirect
github.com/lucas-clemente/quic-go v0.11.1 // indirect
github.com/mattn/go-sqlite3 v1.11.0 // indirect
@@ -79,7 +87,7 @@ require (
github.com/miekg/dns v1.1.9 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
github.com/onsi/ginkgo v1.8.0
github.com/onsi/gomega v1.5.0
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
@@ -87,11 +95,15 @@ require (
github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/prometheus/client_golang v0.9.2 // indirect
github.com/projectcalico/go-json v0.0.0-20161128004156-6219dc7339ba // indirect
github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef // indirect
github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee // indirect
github.com/projectcalico/libcalico-go v0.0.0-20190708183129-ac36d966132f
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
github.com/prometheus/common v0.4.0 // indirect
github.com/prometheus/common v0.4.0
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 // indirect
github.com/russross/blackfriday v1.5.2 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/soheilhy/cmux v0.1.4 // indirect
github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009
github.com/speps/go-hashids v2.0.0+incompatible
@@ -105,12 +117,15 @@ require (
go.uber.org/atomic v1.4.0 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.10.0 // indirect
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
golang.org/x/tools/gopls v0.1.3 // indirect
google.golang.org/appengine v1.5.0 // indirect
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
gopkg.in/igm/sockjs-go.v2 v2.0.0
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/square/go-jose.v2 v2.3.1 // indirect

117
go.sum
View File

@@ -1,9 +1,24 @@
bitbucket.org/ww/goautoneg v0.0.0-20120707110453-75cd24fc2f2c h1:t+Ra932MCC0eeyD/vigXqMbZTzgZjd4JOfBJWC6VSMI=
bitbucket.org/ww/goautoneg v0.0.0-20120707110453-75cd24fc2f2c/go.mod h1:1vhO7Mn/FZMgOgDVGLy5X1mE6rq1HbkBdkF/yj8zkcg=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc=
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest/autorest v0.5.0 h1:Mlm9qy2fpQ9MvfyI41G2Zf5B4CsgjjNbLOWszfK6KrY=
github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw=
github.com/Azure/go-autorest/autorest/adal v0.2.0 h1:7IBDu1jgh+ADHXnEYExkV9RE/ztOOlxdACkkPRthGKw=
github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E=
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.1.0 h1:TRBxC5Pj/fIuh4Qob0ZpkggbfT8RC0SubHbpV3p4/Vc=
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
@@ -12,18 +27,25 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30 h1:Kn3rqvbUFqSepE2OqVu0Pn1CbDw9IuMlONapol0zuwk=
github.com/appscode/jsonpatch v0.0.0-20190108182946-7c0e3b262f30/go.mod h1:4AJxUpXUhv4N+ziTvIcWWXgeorXpxPZOfk9HdEVr96M=
github.com/asaskevich/govalidator v0.0.0-20180315120708-ccb8e960c48f h1:y2hSFdXeA1y5z5f0vfNO0Dg5qVY036qzlz3Pds0B92o=
@@ -37,6 +59,8 @@ github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
@@ -70,6 +94,9 @@ github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKG
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/elazarl/goproxy v0.0.0-20190711103511-473e67f1d7d2 h1:aZtFdDNWY/yH86JPR2WX/PN63635VsE/f/nXNPAbYxY=
@@ -108,14 +135,29 @@ github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/zapr v0.1.1 h1:qXBXPDdNncunGs7XeEpsJt8wCjYBygluzfdLO0G5baE=
github.com/go-logr/zapr v0.1.1/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.0 h1:FTUMcX77w5rQkClIzDtTxvn6Bsa894CcrzNj2MMfeg8=
github.com/go-openapi/jsonpointer v0.19.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.0 h1:Kg7Wl7LkTPlmc393QZQ/5rQadPhi7pBVEMZxyTi0Ii8=
github.com/go-openapi/swag v0.19.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
@@ -127,6 +169,8 @@ github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6 h1:kumyNm8Vr8cbVm/aLQY
github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6/go.mod h1:K/9g3pPouf13kP5K7pdriQEJAy272R9yXuWuDIEWJTM=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/example v0.0.0-20170904185048-46695d81d1fa h1:iqCQC2Z53KkwGgTN9szyL4q0OQHmuNjeoNnMT6lk66k=
github.com/golang/example v0.0.0-20170904185048-46695d81d1fa/go.mod h1:tO/5UvQ/uKigUjQBPqzstj6uxd3fUIjddi19DxGJeWg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
@@ -139,6 +183,7 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
@@ -151,6 +196,10 @@ 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/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.3.0 h1:6sjpKIpVwRIIwmcEGp+WwNovNsem+c+2vm6oxshRpL8=
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
@@ -161,6 +210,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmo
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
@@ -168,6 +218,7 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
@@ -188,8 +239,11 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
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-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
@@ -215,6 +269,8 @@ github.com/kubesphere/sonargo v0.0.2 h1:hsSRE3sv3mkPcUAeSABdp7rtfcNW2zzeHXzFa01C
github.com/kubesphere/sonargo v0.0.2/go.mod h1:ww8n9ANlDXhX5PBZ18iaRnCgEkXN0GMml3/KZXOZ11w=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04=
@@ -223,6 +279,8 @@ github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H7
github.com/lucas-clemente/quic-go v0.11.1 h1:zasajC848Dqq/+WqfqBCkmPw+YHNe1MBts/z7y7nXf4=
github.com/lucas-clemente/quic-go v0.11.1/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
@@ -244,6 +302,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -262,35 +322,49 @@ github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2i
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs=
github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709 h1:zNBQb37RGLmJybyMcs983HfUfpkw9OTFD9tbBfAViHE=
github.com/pborman/uuid v0.0.0-20180906182336-adf5a7427709/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/projectcalico/go-json v0.0.0-20161128004156-6219dc7339ba h1:aaF2byUCZhzszHsfPEr2M3qcU4ibtD/yk/il2R7T1PU=
github.com/projectcalico/go-json v0.0.0-20161128004156-6219dc7339ba/go.mod h1:q8EdCgBdMQzgiX/uk4GXLWLk+gIHd1a7mWUAamJKDb4=
github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef h1:Di9BaA9apb6DEstin8RdhKmlzQG76UMbmjPzjCVkMpc=
github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef/go.mod h1:1Ra2BftSa7Go38Gbq1q0bfmBFSSgUv+Cdc3SY8IL/C0=
github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee h1:yVWsNSlAuYoJ0CznHsYRPiFgsotoj07k00k5rQvGlHM=
github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee/go.mod h1:UgC0aTQ2KMDxlX3lU/stndk7DMUBJqzN40yFiILHgxc=
github.com/projectcalico/libcalico-go v0.0.0-20190708183129-ac36d966132f h1:ccdS7T4NhdlHx8nXe6GiS7TAJUg6Gu/qEDJf1IJvcy8=
github.com/projectcalico/libcalico-go v0.0.0-20190708183129-ac36d966132f/go.mod h1:0b/n/rPzNXjhn4ywFcEJuQdA/5olt9UxFIATz57xkbc=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+CiwcpGTW6pL6bv6KI3KbyEyCKyS+1JWS2h8PNDnGA=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
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=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
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=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
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/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
@@ -328,6 +402,9 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
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/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
@@ -336,20 +413,23 @@ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/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=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -359,12 +439,16 @@ golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -374,33 +458,51 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/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-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c h1:vamGzbGri8IKo20MQncCuljcQ5uAO6kaCeawQPVblAI=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138 h1:H3uGjxCR/6Ds0Mjgyp7LMK81+LvmbvWWEnJhzk1Pi9E=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 h1:QjA/9ArTfVTLfEhClDCG7SGrZkZixxWpwNCDiwJfh88=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190710153321-831012c29e42 h1:4IOeC7p+OItq3+O5BWkcmVu2uBe3jekXau5S4QZX9DU=
golang.org/x/tools v0.0.0-20190710153321-831012c29e42/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools/gopls v0.1.3 h1:CB5ECiPysqZrwxcyRjN+exyZpY0gODTZvNiqQi3lpeo=
golang.org/x/tools/gopls v0.1.3/go.mod h1:vrCQzOKxvuiZLjCKSmbbov04oeBQQOb4VQqwYK2PWIY=
google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
@@ -409,6 +511,10 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/igm/sockjs-go.v2 v2.0.0 h1:NfDyi1jrF9v2VOPESefhKH1NRqpoE9tp4v6kxVR3ubs=
gopkg.in/igm/sockjs-go.v2 v2.0.0/go.mod h1:xvdpHZ3OpjP0TzQzl+174DglrrnYZKVd6qHPIX20Z1Q=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
@@ -436,6 +542,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20181213150558-05914d821849 h1:WZFcFPXmLR7g5CxQNmjWv0mg8qulJLxDghbzS4pQtzY=
k8s.io/api v0.0.0-20181213150558-05914d821849/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=

View File

@@ -18,6 +18,7 @@ set -o errexit
set -o nounset
set -o pipefail
GOPATH=`go env GOPATH`
# generate-groups generates everything for a project with external types only, e.g. a project based
# on CustomResourceDefinitions.

View File

@@ -40,6 +40,6 @@ BUILD_GOARCH=${GOARCH:-amd64}
GOBINARY=${GOBINARY:-go}
# forgoing -i (incremental build) because it will be deprecated by tool chain.
time GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} ${GOBINARY} build \
time GOOS=${BUILD_GOOS} CGO_ENABLED=0 GOARCH=${BUILD_GOARCH} ${GOBINARY} build \
-o ${OUT} \
${BUILDPATH}

11
kustomize/network/OWNERS Normal file
View File

@@ -0,0 +1,11 @@
approvers:
- magicsong
- zheng1
reviewers:
- magicsong
- zheng1
labels:
- area/deploy
- area/networking

View File

@@ -0,0 +1,711 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
name: namespacenetworkpolicies.network.kubesphere.io
spec:
group: network.kubesphere.io
names:
categories:
- networking
kind: NamespaceNetworkPolicy
plural: namespacenetworkpolicies
shortNames:
- nsnp
scope: Namespaced
validation:
openAPIV3Schema:
description: NamespaceNetworkPolicy is the Schema for the namespacenetworkpolicies
API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: NamespaceNetworkPolicySpec defines the desired state of NamespaceNetworkPolicy
properties:
egress:
description: The ordered set of egress rules. Each rule contains a
set of packet match criteria and a corresponding action to apply.
items:
description: "A Rule encapsulates a set of match criteria and an action.
\ Both selector-based security Policy and security Profiles reference
rules - separated out as a list of rules for both ingress and egress
packet matching. \n Each positive match criteria has a negated version,
prefixed with ”Not”. All the match criteria within a rule must be
satisfied for a packet to match. A single rule can contain the positive
and negative version of a match and both must be satisfied for the
rule to match."
properties:
action:
type: string
destination:
description: Destination contains the match criteria that apply
to destination entity.
properties:
namespaceSelector:
description: "NamespaceSelector is an optional field that
contains a selector expression. Only traffic that originates
from (or terminates at) endpoints within the selected namespaces
will be matched. When both NamespaceSelector and Selector
are defined on the same rule, then only workload endpoints
that are matched by both selectors will be selected by the
rule. \n For NetworkPolicy, an empty NamespaceSelector implies
that the Selector is limited to selecting only workload
endpoints in the same namespace as the NetworkPolicy. \n
For GlobalNetworkPolicy, an empty NamespaceSelector implies
the Selector applies to workload endpoints across all namespaces."
type: string
nets:
description: Nets is an optional field that restricts the
rule to only apply to traffic that originates from (or terminates
at) IP addresses in any of the given subnets.
items:
type: string
type: array
notNets:
description: NotNets is the negated version of the Nets field.
items:
type: string
type: array
notPorts:
items:
type: object
x-kubernetes-int-or-string: true
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
description: NotPorts is the negated version of the Ports
field. Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to "TCP" or "UDP".
type: array
notSelector:
description: NotSelector is the negated version of the Selector
field. See Selector field for subtleties with negated selectors.
type: string
ports:
description: "Ports is an optional field that restricts the
rule to only apply to traffic that has a source (destination)
port that matches one of these ranges/values. This value
is a list of integers or strings that represent ranges of
ports. \n Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to \"TCP\" or \"UDP\"."
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
type: object
type: array
selector:
description: "Selector is an optional field that contains
a selector expression (see Policy for sample syntax). Only
traffic that originates from (terminates at) endpoints matching
the selector will be matched. \n Note that: in addition
to the negated version of the Selector (see NotSelector
below), the selector expression syntax itself supports negation.
\ The two types of negation are subtly different. One negates
the set of matched endpoints, the other negates the whole
match: \n \tSelector = \"!has(my_label)\" matches packets
that are from other Calico-controlled \tendpoints that do
not have the label “my_label”. \n \tNotSelector = \"has(my_label)\"
matches packets that are not from Calico-controlled \tendpoints
that do have the label “my_label”. \n The effect is that
the latter will accept packets from non-Calico sources whereas
the former is limited to packets from Calico-controlled
endpoints."
type: string
serviceAccounts:
description: ServiceAccounts is an optional field that restricts
the rule to only apply to traffic that originates from (or
terminates at) a pod running as a matching service account.
properties:
names:
description: Names is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
whose name is in the list.
items:
type: string
type: array
selector:
description: Selector is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
that matches the given label selector. If both Names
and Selector are specified then they are AND'ed.
type: string
type: object
type: object
http:
description: HTTP contains match criteria that apply to HTTP requests.
properties:
methods:
description: Methods is an optional field that restricts the
rule to apply only to HTTP requests that use one of the
listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods
are OR'd together.
items:
type: string
type: array
paths:
description: 'Paths is an optional field that restricts the
rule to apply to HTTP requests that use one of the listed
HTTP Paths. Multiple paths are OR''d together. e.g: - exact:
/foo - prefix: /bar NOTE: Each entry may ONLY specify either
a `exact` or a `prefix` match. The validator will check
for it.'
items:
description: 'HTTPPath specifies an HTTP path to match.
It may be either of the form: exact: <path>: which matches
the path exactly or prefix: <path-prefix>: which matches
the path prefix'
properties:
exact:
type: string
prefix:
type: string
type: object
type: array
type: object
icmp:
description: ICMP is an optional field that restricts the rule
to apply to a specific type and code of ICMP traffic. This
should only be specified if the Protocol field is set to "ICMP"
or "ICMPv6".
properties:
code:
description: Match on a specific ICMP code. If specified,
the Type value must also be specified. This is a technical
limitation imposed by the kernels iptables firewall, which
Calico uses to enforce the rule.
type: integer
type:
description: Match on a specific ICMP type. For example a
value of 8 refers to ICMP Echo Request (i.e. pings).
type: integer
type: object
ipVersion:
description: IPVersion is an optional field that restricts the
rule to only match a specific IP version.
type: integer
notICMP:
description: NotICMP is the negated version of the ICMP field.
properties:
code:
description: Match on a specific ICMP code. If specified,
the Type value must also be specified. This is a technical
limitation imposed by the kernels iptables firewall, which
Calico uses to enforce the rule.
type: integer
type:
description: Match on a specific ICMP type. For example a
value of 8 refers to ICMP Echo Request (i.e. pings).
type: integer
type: object
notProtocol:
description: NotProtocol is the negated version of the Protocol
field.
type: string
protocol:
description: "Protocol is an optional field that restricts the
rule to only apply to traffic of a specific IP protocol. Required
if any of the EntityRules contain Ports (because ports only
apply to certain protocols). \n Must be one of these string
values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\"
or an integer in the range 1-255."
type: string
source:
description: Source contains the match criteria that apply to
source entity.
properties:
namespaceSelector:
description: "NamespaceSelector is an optional field that
contains a selector expression. Only traffic that originates
from (or terminates at) endpoints within the selected namespaces
will be matched. When both NamespaceSelector and Selector
are defined on the same rule, then only workload endpoints
that are matched by both selectors will be selected by the
rule. \n For NetworkPolicy, an empty NamespaceSelector implies
that the Selector is limited to selecting only workload
endpoints in the same namespace as the NetworkPolicy. \n
For GlobalNetworkPolicy, an empty NamespaceSelector implies
the Selector applies to workload endpoints across all namespaces."
type: string
nets:
description: Nets is an optional field that restricts the
rule to only apply to traffic that originates from (or terminates
at) IP addresses in any of the given subnets.
items:
type: string
type: array
notNets:
description: NotNets is the negated version of the Nets field.
items:
type: string
type: array
notPorts:
description: NotPorts is the negated version of the Ports
field. Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to "TCP" or "UDP".
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
type: object
type: array
notSelector:
description: NotSelector is the negated version of the Selector
field. See Selector field for subtleties with negated selectors.
type: string
ports:
description: "Ports is an optional field that restricts the
rule to only apply to traffic that has a source (destination)
port that matches one of these ranges/values. This value
is a list of integers or strings that represent ranges of
ports. \n Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to \"TCP\" or \"UDP\"."
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
type: object
type: array
selector:
description: "Selector is an optional field that contains
a selector expression (see Policy for sample syntax). Only
traffic that originates from (terminates at) endpoints matching
the selector will be matched. \n Note that: in addition
to the negated version of the Selector (see NotSelector
below), the selector expression syntax itself supports negation.
\ The two types of negation are subtly different. One negates
the set of matched endpoints, the other negates the whole
match: \n \tSelector = \"!has(my_label)\" matches packets
that are from other Calico-controlled \tendpoints that do
not have the label “my_label”. \n \tNotSelector = \"has(my_label)\"
matches packets that are not from Calico-controlled \tendpoints
that do have the label “my_label”. \n The effect is that
the latter will accept packets from non-Calico sources whereas
the former is limited to packets from Calico-controlled
endpoints."
type: string
serviceAccounts:
description: ServiceAccounts is an optional field that restricts
the rule to only apply to traffic that originates from (or
terminates at) a pod running as a matching service account.
properties:
names:
description: Names is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
whose name is in the list.
items:
type: string
type: array
selector:
description: Selector is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
that matches the given label selector. If both Names
and Selector are specified then they are AND'ed.
type: string
type: object
type: object
required:
- action
type: object
type: array
ingress:
description: The ordered set of ingress rules. Each rule contains a
set of packet match criteria and a corresponding action to apply.
items:
description: "A Rule encapsulates a set of match criteria and an action.
\ Both selector-based security Policy and security Profiles reference
rules - separated out as a list of rules for both ingress and egress
packet matching. \n Each positive match criteria has a negated version,
prefixed with ”Not”. All the match criteria within a rule must be
satisfied for a packet to match. A single rule can contain the positive
and negative version of a match and both must be satisfied for the
rule to match."
properties:
action:
type: string
destination:
description: Destination contains the match criteria that apply
to destination entity.
properties:
namespaceSelector:
description: "NamespaceSelector is an optional field that
contains a selector expression. Only traffic that originates
from (or terminates at) endpoints within the selected namespaces
will be matched. When both NamespaceSelector and Selector
are defined on the same rule, then only workload endpoints
that are matched by both selectors will be selected by the
rule. \n For NetworkPolicy, an empty NamespaceSelector implies
that the Selector is limited to selecting only workload
endpoints in the same namespace as the NetworkPolicy. \n
For GlobalNetworkPolicy, an empty NamespaceSelector implies
the Selector applies to workload endpoints across all namespaces."
type: string
nets:
description: Nets is an optional field that restricts the
rule to only apply to traffic that originates from (or terminates
at) IP addresses in any of the given subnets.
items:
type: string
type: array
notNets:
description: NotNets is the negated version of the Nets field.
items:
type: string
type: array
notPorts:
description: NotPorts is the negated version of the Ports
field. Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to "TCP" or "UDP".
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
anyOf:
- type: integer
- type: string
type: array
notSelector:
description: NotSelector is the negated version of the Selector
field. See Selector field for subtleties with negated selectors.
type: string
ports:
description: "Ports is an optional field that restricts the
rule to only apply to traffic that has a source (destination)
port that matches one of these ranges/values. This value
is a list of integers or strings that represent ranges of
ports. \n Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to \"TCP\" or \"UDP\"."
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
anyOf:
- type: integer
- type: string
type: array
selector:
description: "Selector is an optional field that contains
a selector expression (see Policy for sample syntax). Only
traffic that originates from (terminates at) endpoints matching
the selector will be matched. \n Note that: in addition
to the negated version of the Selector (see NotSelector
below), the selector expression syntax itself supports negation.
\ The two types of negation are subtly different. One negates
the set of matched endpoints, the other negates the whole
match: \n \tSelector = \"!has(my_label)\" matches packets
that are from other Calico-controlled \tendpoints that do
not have the label “my_label”. \n \tNotSelector = \"has(my_label)\"
matches packets that are not from Calico-controlled \tendpoints
that do have the label “my_label”. \n The effect is that
the latter will accept packets from non-Calico sources whereas
the former is limited to packets from Calico-controlled
endpoints."
type: string
serviceAccounts:
description: ServiceAccounts is an optional field that restricts
the rule to only apply to traffic that originates from (or
terminates at) a pod running as a matching service account.
properties:
names:
description: Names is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
whose name is in the list.
items:
type: string
type: array
selector:
description: Selector is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
that matches the given label selector. If both Names
and Selector are specified then they are AND'ed.
type: string
type: object
type: object
http:
description: HTTP contains match criteria that apply to HTTP requests.
properties:
methods:
description: Methods is an optional field that restricts the
rule to apply only to HTTP requests that use one of the
listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods
are OR'd together.
items:
type: string
type: array
paths:
description: 'Paths is an optional field that restricts the
rule to apply to HTTP requests that use one of the listed
HTTP Paths. Multiple paths are OR''d together. e.g: - exact:
/foo - prefix: /bar NOTE: Each entry may ONLY specify either
a `exact` or a `prefix` match. The validator will check
for it.'
items:
description: 'HTTPPath specifies an HTTP path to match.
It may be either of the form: exact: <path>: which matches
the path exactly or prefix: <path-prefix>: which matches
the path prefix'
properties:
exact:
type: string
prefix:
type: string
type: object
type: array
type: object
icmp:
description: ICMP is an optional field that restricts the rule
to apply to a specific type and code of ICMP traffic. This
should only be specified if the Protocol field is set to "ICMP"
or "ICMPv6".
properties:
code:
description: Match on a specific ICMP code. If specified,
the Type value must also be specified. This is a technical
limitation imposed by the kernels iptables firewall, which
Calico uses to enforce the rule.
type: integer
type:
description: Match on a specific ICMP type. For example a
value of 8 refers to ICMP Echo Request (i.e. pings).
type: integer
type: object
ipVersion:
description: IPVersion is an optional field that restricts the
rule to only match a specific IP version.
type: integer
notICMP:
description: NotICMP is the negated version of the ICMP field.
properties:
code:
description: Match on a specific ICMP code. If specified,
the Type value must also be specified. This is a technical
limitation imposed by the kernels iptables firewall, which
Calico uses to enforce the rule.
type: integer
type:
description: Match on a specific ICMP type. For example a
value of 8 refers to ICMP Echo Request (i.e. pings).
type: integer
type: object
notProtocol:
description: NotProtocol is the negated version of the Protocol
field.
type: string
protocol:
description: "Protocol is an optional field that restricts the
rule to only apply to traffic of a specific IP protocol. Required
if any of the EntityRules contain Ports (because ports only
apply to certain protocols). \n Must be one of these string
values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\"
or an integer in the range 1-255."
type: string
source:
description: Source contains the match criteria that apply to
source entity.
properties:
namespaceSelector:
description: "NamespaceSelector is an optional field that
contains a selector expression. Only traffic that originates
from (or terminates at) endpoints within the selected namespaces
will be matched. When both NamespaceSelector and Selector
are defined on the same rule, then only workload endpoints
that are matched by both selectors will be selected by the
rule. \n For NetworkPolicy, an empty NamespaceSelector implies
that the Selector is limited to selecting only workload
endpoints in the same namespace as the NetworkPolicy. \n
For GlobalNetworkPolicy, an empty NamespaceSelector implies
the Selector applies to workload endpoints across all namespaces."
type: string
nets:
description: Nets is an optional field that restricts the
rule to only apply to traffic that originates from (or terminates
at) IP addresses in any of the given subnets.
items:
type: string
type: array
notNets:
description: NotNets is the negated version of the Nets field.
items:
type: string
type: array
notPorts:
description: NotPorts is the negated version of the Ports
field. Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to "TCP" or "UDP".
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
type: object
type: array
notSelector:
description: NotSelector is the negated version of the Selector
field. See Selector field for subtleties with negated selectors.
type: string
ports:
description: "Ports is an optional field that restricts the
rule to only apply to traffic that has a source (destination)
port that matches one of these ranges/values. This value
is a list of integers or strings that represent ranges of
ports. \n Since only some protocols have ports, if any ports
are specified it requires the Protocol match in the Rule
to be set to \"TCP\" or \"UDP\"."
items:
description: "Port represents either a range of numeric
ports or a named port. \n - For a named port, set
the PortName, leaving MinPort and MaxPort as 0. -
For a port range, set MinPort and MaxPort to the (inclusive)
port numbers. Set PortName to \"\". - For a
single port, set MinPort = MaxPort and PortName = \"\"."
x-kubernetes-int-or-string: true
anyOf:
- type: integer
- type: string
type: object
type: array
selector:
description: "Selector is an optional field that contains
a selector expression (see Policy for sample syntax). Only
traffic that originates from (terminates at) endpoints matching
the selector will be matched. \n Note that: in addition
to the negated version of the Selector (see NotSelector
below), the selector expression syntax itself supports negation.
\ The two types of negation are subtly different. One negates
the set of matched endpoints, the other negates the whole
match: \n \tSelector = \"!has(my_label)\" matches packets
that are from other Calico-controlled \tendpoints that do
not have the label “my_label”. \n \tNotSelector = \"has(my_label)\"
matches packets that are not from Calico-controlled \tendpoints
that do have the label “my_label”. \n The effect is that
the latter will accept packets from non-Calico sources whereas
the former is limited to packets from Calico-controlled
endpoints."
type: string
serviceAccounts:
description: ServiceAccounts is an optional field that restricts
the rule to only apply to traffic that originates from (or
terminates at) a pod running as a matching service account.
properties:
names:
description: Names is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
whose name is in the list.
items:
type: string
type: array
selector:
description: Selector is an optional field that restricts
the rule to only apply to traffic that originates from
(or terminates at) a pod running as a service account
that matches the given label selector. If both Names
and Selector are specified then they are AND'ed.
type: string
type: object
type: object
required:
- action
type: object
type: array
order:
description: Order is an optional field that specifies the order in
which the policy is applied. Policies with higher "order" are applied
after those with lower order. If the order is omitted, it may be
considered to be "infinite" - i.e. the policy will be applied last. Policies
with identical order will be applied in alphanumerical order based
on the Policy "Name".
type: integer
selector:
description: "The selector is an expression used to pick pick out the
endpoints that the policy should be applied to. \n Selector expressions
follow this syntax: \n \tlabel == \"string_literal\" -> comparison,
e.g. my_label == \"foo bar\" \tlabel != \"string_literal\" -> not
equal; also matches if label is not present \tlabel in { \"a\", \"b\",
\"c\", ... } -> true if the value of label X is one of \"a\", \"b\",
\"c\" \tlabel not in { \"a\", \"b\", \"c\", ... } -> true if the
value of label X is not one of \"a\", \"b\", \"c\" \thas(label_name)
\ -> True if that label is present \t! expr -> negation of expr \texpr
&& expr -> Short-circuit and \texpr || expr -> Short-circuit or
\t( expr ) -> parens for grouping \tall() or the empty selector ->
matches all endpoints. \n Label names are allowed to contain alphanumerics,
-, _ and /. String literals are more permissive but they do not support
escape characters. \n Examples (with made-up labels): \n \ttype ==
\"webserver\" && deployment == \"prod\" \ttype in {\"frontend\", \"backend\"}
\tdeployment != \"dev\" \t! has(label_name)"
type: string
types:
description: "Types indicates whether this policy applies to ingress,
or to egress, or to both. When not explicitly specified (and so the
value on creation is empty or nil), Calico defaults Types according
to what Ingress and Egress are present in the policy. The default
is: \n - [ PolicyTypeIngress ], if there are no Egress rules (including
the case where there are also no Ingress rules) \n - [ PolicyTypeEgress
], if there are Egress rules but no Ingress rules \n - [ PolicyTypeIngress,
PolicyTypeEgress ], if there are both Ingress and Egress rules. \n
When the policy is read back again, Types will always be one of these
values, never empty or nil."
items:
type: string
type: array
required:
- selector
type: object
type: object
version: v1alpha1
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@@ -6,9 +6,13 @@ metadata:
spec:
group: network.kubesphere.io
names:
categories:
- networking
kind: WorkspaceNetworkPolicy
plural: workspacenetworkpolicies
scope: "Cluster"
shortNames:
- wsnp
scope: Cluster
validation:
openAPIV3Schema:
description: WorkspaceNetworkPolicy is a set of network policies applied to

View File

@@ -0,0 +1,22 @@
resources:
- network.yaml
- crds/wsnp.yaml
- crds/nsnp.yaml
- rbac/role.yaml
- rbac/role_binding.yaml
generatorOptions:
disableNameSuffixHash: true
secretGenerator:
- name: calico-etcd-secrets
files:
- etcd-ca=etcd/ca
- etcd-key=etcd/key
- etcd-cert=etcd/crt
type: Opaque
patchesStrategicMerge:
- patch_image_name.yaml
namespace: network-test-90fa3885

View File

@@ -0,0 +1,57 @@
apiVersion: v1
kind: Namespace
metadata:
name: network-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: network-manager
namespace: network-system
labels:
control-plane: network-manager
spec:
selector:
matchLabels:
control-plane: network-manager
replicas: 1
template:
metadata:
labels:
control-plane: network-manager
spec:
nodeSelector:
node-role.kubernetes.io/master: ""
hostNetwork: true
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
- key: "node-role.kubernetes.io/master"
effect: NoSchedule
containers:
- command:
- /ks-network
args:
- -v=4
- np-provider=calico
image: network:latest
imagePullPolicy: Always
name: manager
resources:
limits:
cpu: 100m
memory: 30Mi
requests:
cpu: 100m
memory: 20Mi
volumeMounts:
- mountPath: /calicocerts
name: etcd-certs
readOnly: true
terminationGracePeriodSeconds: 10
volumes:
- name: etcd-certs
secret:
secretName: calico-etcd-secrets
defaultMode: 0400

View File

@@ -0,0 +1,12 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: network-manager
namespace: network-system
spec:
template:
spec:
containers:
# Change the value of image field below to your controller image URL
- image: magicsong/ks-network:90fa3885
name: manager

View File

@@ -0,0 +1,8 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: manager-rolebinding
subjects:
- kind: ServiceAccount
name: default
namespace: network-test-90fa3885

View File

@@ -0,0 +1,30 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: net-manager-role
rules:
- apiGroups:
- network.kubesphere.io
resources:
- namespacenetworkpolicies
- workspacenetworkpolicies
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- tenant.kubesphere.io
resources:
- workspaces
verbs:
- create
- delete
- get
- list
- patch
- update
- watch

View File

@@ -0,0 +1,12 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: net-manager-role
subjects:
- kind: ServiceAccount
name: default
namespace: network-system

View File

@@ -0,0 +1,170 @@
package v1alpha1
import (
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring"
corev1 "k8s.io/api/core/v1"
)
// A Rule encapsulates a set of match criteria and an action. Both selector-based security Policy
// and security Profiles reference rules - separated out as a list of rules for both
// ingress and egress packet matching.
//
// Each positive match criteria has a negated version, prefixed with ”Not”. All the match
// criteria within a rule must be satisfied for a packet to match. A single rule can contain
// the positive and negative version of a match and both must be satisfied for the rule to match.
type Rule struct {
Action Action `json:"action" validate:"action"`
// IPVersion is an optional field that restricts the rule to only match a specific IP
// version.
IPVersion *int `json:"ipVersion,omitempty" validate:"omitempty,ipVersion"`
// Protocol is an optional field that restricts the rule to only apply to traffic of
// a specific IP protocol. Required if any of the EntityRules contain Ports
// (because ports only apply to certain protocols).
//
// Must be one of these string values: "TCP", "UDP", "ICMP", "ICMPv6", "SCTP", "UDPLite"
// or an integer in the range 1-255.
Protocol *corev1.Protocol `json:"protocol,omitempty" validate:"omitempty"`
// ICMP is an optional field that restricts the rule to apply to a specific type and
// code of ICMP traffic. This should only be specified if the Protocol field is set to
// "ICMP" or "ICMPv6".
ICMP *ICMPFields `json:"icmp,omitempty" validate:"omitempty"`
// NotProtocol is the negated version of the Protocol field.
NotProtocol *corev1.Protocol `json:"notProtocol,omitempty" validate:"omitempty"`
// NotICMP is the negated version of the ICMP field.
NotICMP *ICMPFields `json:"notICMP,omitempty" validate:"omitempty"`
// Source contains the match criteria that apply to source entity.
Source EntityRule `json:"source,omitempty" validate:"omitempty"`
// Destination contains the match criteria that apply to destination entity.
Destination EntityRule `json:"destination,omitempty" validate:"omitempty"`
// HTTP contains match criteria that apply to HTTP requests.
HTTP *HTTPMatch `json:"http,omitempty" validate:"omitempty"`
}
// HTTPPath specifies an HTTP path to match. It may be either of the form:
// exact: <path>: which matches the path exactly or
// prefix: <path-prefix>: which matches the path prefix
type HTTPPath struct {
Exact string `json:"exact,omitempty" validate:"omitempty"`
Prefix string `json:"prefix,omitempty" validate:"omitempty"`
}
// HTTPMatch is an optional field that apply only to HTTP requests
// The Methods and Path fields are joined with AND
type HTTPMatch struct {
// Methods is an optional field that restricts the rule to apply only to HTTP requests that use one of the listed
// HTTP Methods (e.g. GET, PUT, etc.)
// Multiple methods are OR'd together.
Methods []string `json:"methods,omitempty" validate:"omitempty"`
// Paths is an optional field that restricts the rule to apply to HTTP requests that use one of the listed
// HTTP Paths.
// Multiple paths are OR'd together.
// e.g:
// - exact: /foo
// - prefix: /bar
// NOTE: Each entry may ONLY specify either a `exact` or a `prefix` match. The validator will check for it.
Paths []HTTPPath `json:"paths,omitempty" validate:"omitempty"`
}
// ICMPFields defines structure for ICMP and NotICMP sub-struct for ICMP code and type
type ICMPFields struct {
// Match on a specific ICMP type. For example a value of 8 refers to ICMP Echo Request
// (i.e. pings).
Type *int `json:"type,omitempty" validate:"omitempty,gte=0,lte=254"`
// Match on a specific ICMP code. If specified, the Type value must also be specified.
// This is a technical limitation imposed by the kernels iptables firewall, which
// Calico uses to enforce the rule.
Code *int `json:"code,omitempty" validate:"omitempty,gte=0,lte=255"`
}
// An EntityRule is a sub-component of a Rule comprising the match criteria specific
// to a particular entity (that is either the source or destination).
//
// A source EntityRule matches the source endpoint and originating traffic.
// A destination EntityRule matches the destination endpoint and terminating traffic.
type EntityRule struct {
// Nets is an optional field that restricts the rule to only apply to traffic that
// originates from (or terminates at) IP addresses in any of the given subnets.
Nets []string `json:"nets,omitempty" validate:"omitempty,dive,net"`
// Selector is an optional field that contains a selector expression (see Policy for
// sample syntax). Only traffic that originates from (terminates at) endpoints matching
// the selector will be matched.
//
// Note that: in addition to the negated version of the Selector (see NotSelector below), the
// selector expression syntax itself supports negation. The two types of negation are subtly
// different. One negates the set of matched endpoints, the other negates the whole match:
//
// Selector = "!has(my_label)" matches packets that are from other Calico-controlled
// endpoints that do not have the label “my_label”.
//
// NotSelector = "has(my_label)" matches packets that are not from Calico-controlled
// endpoints that do have the label “my_label”.
//
// The effect is that the latter will accept packets from non-Calico sources whereas the
// former is limited to packets from Calico-controlled endpoints.
Selector string `json:"selector,omitempty" validate:"omitempty,selector"`
// NamespaceSelector is an optional field that contains a selector expression. Only traffic
// that originates from (or terminates at) endpoints within the selected namespaces will be
// matched. When both NamespaceSelector and Selector are defined on the same rule, then only
// workload endpoints that are matched by both selectors will be selected by the rule.
//
// For NetworkPolicy, an empty NamespaceSelector implies that the Selector is limited to selecting
// only workload endpoints in the same namespace as the NetworkPolicy.
//
// For GlobalNetworkPolicy, an empty NamespaceSelector implies the Selector applies to workload
// endpoints across all namespaces.
NamespaceSelector string `json:"namespaceSelector,omitempty" validate:"omitempty,selector"`
// Ports is an optional field that restricts the rule to only apply to traffic that has a
// source (destination) port that matches one of these ranges/values. This value is a
// list of integers or strings that represent ranges of ports.
//
// Since only some protocols have ports, if any ports are specified it requires the
// Protocol match in the Rule to be set to "TCP" or "UDP".
Ports []numorstring.Port `json:"ports,omitempty" validate:"omitempty,dive"`
// NotNets is the negated version of the Nets field.
NotNets []string `json:"notNets,omitempty" validate:"omitempty,dive,net"`
// NotSelector is the negated version of the Selector field. See Selector field for
// subtleties with negated selectors.
NotSelector string `json:"notSelector,omitempty" validate:"omitempty,selector"`
// NotPorts is the negated version of the Ports field.
// Since only some protocols have ports, if any ports are specified it requires the
// Protocol match in the Rule to be set to "TCP" or "UDP".
NotPorts []numorstring.Port `json:"notPorts,omitempty" validate:"omitempty,dive"`
// ServiceAccounts is an optional field that restricts the rule to only apply to traffic that originates from (or
// terminates at) a pod running as a matching service account.
ServiceAccounts *ServiceAccountMatch `json:"serviceAccounts,omitempty" validate:"omitempty"`
}
type ServiceAccountMatch struct {
// Names is an optional field that restricts the rule to only apply to traffic that originates from (or terminates
// at) a pod running as a service account whose name is in the list.
Names []string `json:"names,omitempty" validate:"omitempty"`
// Selector is an optional field that restricts the rule to only apply to traffic that originates from
// (or terminates at) a pod running as a service account that matches the given label selector.
// If both Names and Selector are specified then they are AND'ed.
Selector string `json:"selector,omitempty" validate:"omitempty,selector"`
}
type Action string
const (
Allow Action = "Allow"
Deny = "Deny"
Log = "Log"
Pass = "Pass"
)
type PolicyType string
const (
PolicyTypeIngress PolicyType = "Ingress"
PolicyTypeEgress PolicyType = "Egress"
)

View File

@@ -0,0 +1,108 @@
/*
Copyright 2019 The KubeSphere 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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// All types in this file is copy from calicoapi as we use calico to policy
// NamespaceNetworkPolicySpec defines the desired state of NamespaceNetworkPolicy
type NamespaceNetworkPolicySpec struct {
// Order is an optional field that specifies the order in which the policy is applied.
// Policies with higher "order" are applied after those with lower
// order. If the order is omitted, it may be considered to be "infinite" - i.e. the
// policy will be applied last. Policies with identical order will be applied in
// alphanumerical order based on the Policy "Name".
Order *int `json:"order,omitempty"`
// The ordered set of ingress rules. Each rule contains a set of packet match criteria and
// a corresponding action to apply.
Ingress []Rule `json:"ingress,omitempty" validate:"omitempty,dive"`
// The ordered set of egress rules. Each rule contains a set of packet match criteria and
// a corresponding action to apply.
Egress []Rule `json:"egress,omitempty" validate:"omitempty,dive"`
// The selector is an expression used to pick pick out the endpoints that the policy should
// be applied to.
//
// Selector expressions follow this syntax:
//
// label == "string_literal" -> comparison, e.g. my_label == "foo bar"
// label != "string_literal" -> not equal; also matches if label is not present
// label in { "a", "b", "c", ... } -> true if the value of label X is one of "a", "b", "c"
// label not in { "a", "b", "c", ... } -> true if the value of label X is not one of "a", "b", "c"
// has(label_name) -> True if that label is present
// ! expr -> negation of expr
// expr && expr -> Short-circuit and
// expr || expr -> Short-circuit or
// ( expr ) -> parens for grouping
// all() or the empty selector -> matches all endpoints.
//
// Label names are allowed to contain alphanumerics, -, _ and /. String literals are more permissive
// but they do not support escape characters.
//
// Examples (with made-up labels):
//
// type == "webserver" && deployment == "prod"
// type in {"frontend", "backend"}
// deployment != "dev"
// ! has(label_name)
Selector string `json:"selector" validate:"selector"`
// Types indicates whether this policy applies to ingress, or to egress, or to both. When
// not explicitly specified (and so the value on creation is empty or nil), Calico defaults
// Types according to what Ingress and Egress are present in the policy. The
// default is:
//
// - [ PolicyTypeIngress ], if there are no Egress rules (including the case where there are
// also no Ingress rules)
//
// - [ PolicyTypeEgress ], if there are Egress rules but no Ingress rules
//
// - [ PolicyTypeIngress, PolicyTypeEgress ], if there are both Ingress and Egress rules.
//
// When the policy is read back again, Types will always be one of these values, never empty
// or nil.
Types []PolicyType `json:"types,omitempty" validate:"omitempty,dive,policyType"`
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// NamespaceNetworkPolicy is the Schema for the namespacenetworkpolicies API
// +k8s:openapi-gen=true
// +kubebuilder:resource:categories="networking",shortName="nsnp"
type NamespaceNetworkPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec NamespaceNetworkPolicySpec `json:"spec,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// NamespaceNetworkPolicyList contains a list of NamespaceNetworkPolicy
type NamespaceNetworkPolicyList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []NamespaceNetworkPolicy `json:"items"`
}
func init() {
SchemeBuilder.Register(&NamespaceNetworkPolicy{}, &NamespaceNetworkPolicyList{})
}

View File

@@ -0,0 +1,58 @@
/*
Copyright 2019 The KubeSphere 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 v1alpha1
import (
"testing"
"github.com/onsi/gomega"
"golang.org/x/net/context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
func TestStorageNamespaceNetworkPolicy(t *testing.T) {
key := types.NamespacedName{
Name: "foo",
Namespace: "default",
}
created := &NamespaceNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "default",
}}
g := gomega.NewGomegaWithT(t)
// Test Create
fetched := &NamespaceNetworkPolicy{}
g.Expect(c.Create(context.TODO(), created)).To(gomega.Succeed())
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed())
g.Expect(fetched).To(gomega.Equal(created))
// Test Updating the Labels
updated := fetched.DeepCopy()
updated.Labels = map[string]string{"hello": "world"}
g.Expect(c.Update(context.TODO(), updated)).To(gomega.Succeed())
g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed())
g.Expect(fetched).To(gomega.Equal(updated))
// Test Delete
g.Expect(c.Delete(context.TODO(), fetched)).To(gomega.Succeed())
g.Expect(c.Get(context.TODO(), key, fetched)).ToNot(gomega.Succeed())
}

View File

@@ -0,0 +1,73 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// 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 numorstring
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
)
type ASNumber uint32
// ASNumberFromString creates an ASNumber struct from a string value. The
// string value may simply be a number or may be the ASN in dotted notation.
func ASNumberFromString(s string) (ASNumber, error) {
if num, err := strconv.ParseUint(s, 10, 32); err == nil {
return ASNumber(num), nil
}
parts := strings.Split(s, ".")
if len(parts) != 2 {
msg := fmt.Sprintf("invalid AS Number format (%s)", s)
return 0, errors.New(msg)
}
if num1, err := strconv.ParseUint(parts[0], 10, 16); err != nil {
msg := fmt.Sprintf("invalid AS Number format (%s)", s)
return 0, errors.New(msg)
} else if num2, err := strconv.ParseUint(parts[1], 10, 16); err != nil {
msg := fmt.Sprintf("invalid AS Number format (%s)", s)
return 0, errors.New(msg)
} else {
return ASNumber((num1 << 16) + num2), nil
}
}
// UnmarshalJSON implements the json.Unmarshaller uinterface.
func (a *ASNumber) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, (*uint32)(a)); err == nil {
return nil
} else {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
if v, err := ASNumberFromString(s); err != nil {
return err
} else {
*a = v
return nil
}
}
}
// String returns the string value, or the Itoa of the uint value.
func (a ASNumber) String() string {
return strconv.FormatUint(uint64(a), 10)
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// 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 numorstring implements a set of type definitions that in YAML or JSON
format may be represented by either a number or a string.
*/
package numorstring

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2016,2018 Tigera, Inc. All rights reserved.
// 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 numorstring_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)
func TestNumorstring(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Numorstring Suite")
}

View File

@@ -0,0 +1,204 @@
// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved.
// 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 numorstring_test
import (
"encoding/json"
"fmt"
"reflect"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
"github.com/projectcalico/libcalico-go/lib/numorstring"
)
func init() {
asNumberType := reflect.TypeOf(numorstring.ASNumber(0))
protocolType := reflect.TypeOf(numorstring.Protocol{})
portType := reflect.TypeOf(numorstring.Port{})
// Perform tests of JSON unmarshaling of the various field types.
DescribeTable("NumOrStringJSONUnmarshaling",
func(jtext string, typ reflect.Type, expected interface{}) {
// Create a new field type and invoke the unmarshaller interface
// directly (this covers a couple more error cases than calling
// through json.Unmarshal.
new := reflect.New(typ)
u := new.Interface().(json.Unmarshaler)
err := u.UnmarshalJSON([]byte(jtext))
if expected != nil {
Expect(err).To(BeNil(),
"expected json unmarshal to not error")
Expect(new.Elem().Interface()).To(Equal(expected),
"expected value not same as json unmarshalled value")
} else {
Expect(err).ToNot(BeNil(),
"expected json unmarshal to error")
}
},
// ASNumber tests.
Entry("should accept 0 AS number as int", "0", asNumberType, numorstring.ASNumber(0)),
Entry("should accept 4294967295 AS number as int", "4294967295", asNumberType, numorstring.ASNumber(4294967295)),
Entry("should accept 0 AS number as string", "\"0\"", asNumberType, numorstring.ASNumber(0)),
Entry("should accept 4294967295 AS number as string", "\"4294967295\"", asNumberType, numorstring.ASNumber(4294967295)),
Entry("should accept 1.10 AS number as string", "\"1.10\"", asNumberType, numorstring.ASNumber(65546)),
Entry("should accept 00.00 AS number as string", "\"00.00\"", asNumberType, numorstring.ASNumber(0)),
Entry("should accept 00.01 AS number as string", "\"00.01\"", asNumberType, numorstring.ASNumber(1)),
Entry("should accept 65535.65535 AS number as string", "\"65535.65535\"", asNumberType, numorstring.ASNumber(4294967295)),
Entry("should reject 1.1.1 AS number as string", "\"1.1.1\"", asNumberType, nil),
Entry("should reject 65536.65535 AS number as string", "\"65536.65535\"", asNumberType, nil),
Entry("should reject 65535.65536 AS number as string", "\"65535.65536\"", asNumberType, nil),
Entry("should reject 0.-1 AS number as string", "\"0.-1\"", asNumberType, nil),
Entry("should reject -1 AS number as int", "-1", asNumberType, nil),
Entry("should reject 4294967296 AS number as int", "4294967296", asNumberType, nil),
// Port tests.
Entry("should accept 0 port as int", "0", portType, numorstring.SinglePort(0)),
Entry("should accept 65535 port as int", "65535", portType, numorstring.SinglePort(65535)),
Entry("should accept 0:65535 port range as string", "\"0:65535\"", portType, portFromRange(0, 65535)),
Entry("should accept 1:10 port range as string", "\"1:10\"", portType, portFromRange(1, 10)),
Entry("should accept foo-bar as named port", "\"foo-bar\"", portType, numorstring.NamedPort("foo-bar")),
Entry("should reject -1 port as int", "-1", portType, nil),
Entry("should reject 65536 port as int", "65536", portType, nil),
Entry("should reject 0:65536 port range as string", "\"0:65536\"", portType, nil),
Entry("should reject -1:65535 port range as string", "\"-1:65535\"", portType, nil),
Entry("should reject 10:1 port range as string", "\"10:1\"", portType, nil),
Entry("should reject 1:2:3 port range as string", "\"1:2:3\"", portType, nil),
Entry("should reject bad named port string", "\"*\"", portType, nil),
Entry("should reject bad port string", "\"1:2", portType, nil),
// Protocol tests. Invalid integer values will be stored as strings.
Entry("should accept 0 protocol as int", "0", protocolType, numorstring.ProtocolFromInt(0)),
Entry("should accept 255 protocol as int", "255", protocolType, numorstring.ProtocolFromInt(255)),
Entry("should accept tcp protocol as string", "\"TCP\"", protocolType, numorstring.ProtocolFromString("TCP")),
Entry("should accept tcp protocol as string", "\"TCP\"", protocolType, numorstring.ProtocolFromString("TCP")),
Entry("should accept 0 protocol as string", "\"0\"", protocolType, numorstring.ProtocolFromInt(0)),
Entry("should accept 0 protocol as string", "\"255\"", protocolType, numorstring.ProtocolFromInt(255)),
Entry("should accept 256 protocol as string", "\"256\"", protocolType, numorstring.ProtocolFromString("256")),
Entry("should reject bad protocol string", "\"25", protocolType, nil),
)
// Perform tests of JSON marshaling of the various field types.
DescribeTable("NumOrStringJSONMarshaling",
func(field interface{}, jtext string) {
b, err := json.Marshal(field)
if jtext != "" {
Expect(err).To(BeNil(),
"expected json marshal to not error")
Expect(string(b)).To(Equal(jtext),
"expected json not same as marshalled value")
} else {
Expect(err).ToNot(BeNil(),
"expected json marshal to error")
}
},
// ASNumber tests.
Entry("should marshal ASN of 0", numorstring.ASNumber(0), "0"),
Entry("should marshal ASN of 4294967295", numorstring.ASNumber(4294967295), "4294967295"),
// Port tests.
Entry("should marshal port of 0", numorstring.SinglePort(0), "0"),
Entry("should marshal port of 65535", portFromRange(65535, 65535), "65535"),
Entry("should marshal port of 10", portFromString("10"), "10"),
Entry("should marshal port range of 10:20", portFromRange(10, 20), "\"10:20\""),
Entry("should marshal port range of 20:30", portFromRange(20, 30), "\"20:30\""),
Entry("should marshal named port", numorstring.NamedPort("foobar"), `"foobar"`),
// Protocol tests.
Entry("should marshal protocol of 0", numorstring.ProtocolFromInt(0), "0"),
Entry("should marshal protocol of udp", numorstring.ProtocolFromString("UDP"), "\"UDP\""),
)
// Perform tests of Stringer interface various field types.
DescribeTable("NumOrStringStringify",
func(field interface{}, s string) {
a := fmt.Sprint(field)
Expect(a).To(Equal(s),
"expected String() value to match")
},
// ASNumber tests.
Entry("should stringify ASN of 0", numorstring.ASNumber(0), "0"),
Entry("should stringify ASN of 4294967295", numorstring.ASNumber(4294967295), "4294967295"),
// Port tests.
Entry("should stringify port of 20", numorstring.SinglePort(20), "20"),
Entry("should stringify port range of 10:20", portFromRange(10, 20), "10:20"),
// Protocol tests.
Entry("should stringify protocol of 0", numorstring.ProtocolFromInt(0), "0"),
Entry("should stringify protocol of udp", numorstring.ProtocolFromString("UDP"), "UDP"),
)
// Perform tests of Protocols supporting ports.
DescribeTable("NumOrStringProtocolsSupportingPorts",
func(protocol numorstring.Protocol, supportsPorts bool) {
Expect(protocol.SupportsPorts()).To(Equal(supportsPorts),
"expected protocol port support to match")
},
Entry("protocol 6 supports ports", numorstring.ProtocolFromInt(6), true),
Entry("protocol 17 supports ports", numorstring.ProtocolFromInt(17), true),
Entry("protocol udp supports ports", numorstring.ProtocolFromString("UDP"), true),
Entry("protocol udp supports ports", numorstring.ProtocolFromString("TCP"), true),
Entry("protocol foo does not support ports", numorstring.ProtocolFromString("foo"), false),
Entry("protocol 2 does not support ports", numorstring.ProtocolFromInt(2), false),
)
// Perform tests of Protocols FromString method.
DescribeTable("NumOrStringProtocols FromString is not case sensitive",
func(input, expected string) {
Expect(numorstring.ProtocolFromString(input).StrVal).To(Equal(expected),
"expected parsed protocol to match")
},
Entry("protocol udp -> UDP", "udp", "UDP"),
Entry("protocol tcp -> TCP", "tcp", "TCP"),
Entry("protocol updlite -> UDPLite", "udplite", "UDPLite"),
Entry("unknown protocol xxxXXX", "xxxXXX", "xxxXXX"),
)
// Perform tests of Protocols FromStringV1 method.
DescribeTable("NumOrStringProtocols FromStringV1 is lowercase",
func(input, expected string) {
Expect(numorstring.ProtocolFromStringV1(input).StrVal).To(Equal(expected),
"expected parsed protocol to match")
},
Entry("protocol udp -> UDP", "UDP", "udp"),
Entry("protocol tcp -> TCP", "TCP", "tcp"),
Entry("protocol updlite -> UDPLite", "UDPLite", "udplite"),
Entry("unknown protocol xxxXXX", "xxxXXX", "xxxxxx"),
)
// Perform tests of Protocols ToV1 method.
DescribeTable("NumOrStringProtocols FromStringV1 is lowercase",
func(input, expected numorstring.Protocol) {
Expect(input.ToV1()).To(Equal(expected),
"expected parsed protocol to match")
},
// Protocol tests.
Entry("protocol udp -> UDP", numorstring.ProtocolFromInt(2), numorstring.ProtocolFromInt(2)),
Entry("protocol tcp -> TCP", numorstring.ProtocolFromString("TCP"), numorstring.ProtocolFromStringV1("TCP")),
)
}
func portFromRange(minPort, maxPort uint16) numorstring.Port {
p, _ := numorstring.PortFromRange(minPort, maxPort)
return p
}
func portFromString(s string) numorstring.Port {
p, _ := numorstring.PortFromString(s)
return p
}

View File

@@ -0,0 +1,144 @@
// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved.
//
// 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 numorstring
import (
"encoding/json"
"errors"
"fmt"
"regexp"
"strconv"
)
// Port represents either a range of numeric ports or a named port.
//
// - For a named port, set the PortName, leaving MinPort and MaxPort as 0.
// - For a port range, set MinPort and MaxPort to the (inclusive) port numbers. Set
// PortName to "".
// - For a single port, set MinPort = MaxPort and PortName = "".
type Port struct {
MinPort uint16 `json:"minPort,omitempty"`
MaxPort uint16 `json:"maxPort,omitempty"`
PortName string `validate:"omitempty,portName" json:"portName,omitempty"`
}
// SinglePort creates a Port struct representing a single port.
func SinglePort(port uint16) Port {
return Port{MinPort: port, MaxPort: port}
}
func NamedPort(name string) Port {
return Port{PortName: name}
}
// PortFromRange creates a Port struct representing a range of ports.
func PortFromRange(minPort, maxPort uint16) (Port, error) {
port := Port{MinPort: minPort, MaxPort: maxPort}
if minPort > maxPort {
msg := fmt.Sprintf("minimum port number (%d) is greater than maximum port number (%d) in port range", minPort, maxPort)
return port, errors.New(msg)
}
return port, nil
}
var (
allDigits = regexp.MustCompile(`^\d+$`)
portRange = regexp.MustCompile(`^(\d+):(\d+)$`)
nameRegex = regexp.MustCompile("^[a-zA-Z0-9_.-]{1,128}$")
)
// PortFromString creates a Port struct from its string representation. A port
// may either be single value "1234", a range of values "100:200" or a named port: "name".
func PortFromString(s string) (Port, error) {
if allDigits.MatchString(s) {
// Port is all digits, it should parse as a single port.
num, err := strconv.ParseUint(s, 10, 16)
if err != nil {
msg := fmt.Sprintf("invalid port format (%s)", s)
return Port{}, errors.New(msg)
}
return SinglePort(uint16(num)), nil
}
if groups := portRange.FindStringSubmatch(s); len(groups) > 0 {
// Port matches <digits>:<digits>, it should parse as a range of ports.
if pmin, err := strconv.ParseUint(groups[1], 10, 16); err != nil {
msg := fmt.Sprintf("invalid minimum port number in range (%s)", s)
return Port{}, errors.New(msg)
} else if pmax, err := strconv.ParseUint(groups[2], 10, 16); err != nil {
msg := fmt.Sprintf("invalid maximum port number in range (%s)", s)
return Port{}, errors.New(msg)
} else {
return PortFromRange(uint16(pmin), uint16(pmax))
}
}
if !nameRegex.MatchString(s) {
msg := fmt.Sprintf("invalid name for named port (%s)", s)
return Port{}, errors.New(msg)
}
return NamedPort(s), nil
}
// UnmarshalJSON implements the json.Unmarshaller interface.
func (p *Port) UnmarshalJSON(b []byte) error {
if b[0] == '"' {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
if v, err := PortFromString(s); err != nil {
return err
} else {
*p = v
return nil
}
}
// It's not a string, it must be a single int.
var i uint16
if err := json.Unmarshal(b, &i); err != nil {
return err
}
v := SinglePort(i)
*p = v
return nil
}
// MarshalJSON implements the json.Marshaller interface.
func (p Port) MarshalJSON() ([]byte, error) {
if p.PortName != "" {
return json.Marshal(p.PortName)
} else if p.MinPort == p.MaxPort {
return json.Marshal(p.MinPort)
} else {
return json.Marshal(p.String())
}
}
// String returns the string value. If the min and max port are the same
// this returns a single string representation of the port number, otherwise
// if returns a colon separated range of ports.
func (p Port) String() string {
if p.PortName != "" {
return p.PortName
} else if p.MinPort == p.MaxPort {
return strconv.FormatUint(uint64(p.MinPort), 10)
} else {
return fmt.Sprintf("%d:%d", p.MinPort, p.MaxPort)
}
}

View File

@@ -0,0 +1,134 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// 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 numorstring
import "strings"
const (
ProtocolUDP = "UDP"
ProtocolTCP = "TCP"
ProtocolICMP = "ICMP"
ProtocolICMPv6 = "ICMPv6"
ProtocolSCTP = "SCTP"
ProtocolUDPLite = "UDPLite"
ProtocolUDPV1 = "udp"
ProtocolTCPV1 = "tcp"
)
var (
allProtocolNames = []string{
ProtocolUDP,
ProtocolTCP,
ProtocolICMP,
ProtocolICMPv6,
ProtocolSCTP,
ProtocolUDPLite,
}
)
type Protocol Uint8OrString
// ProtocolFromInt creates a Protocol struct from an integer value.
func ProtocolFromInt(p uint8) Protocol {
return Protocol(
Uint8OrString{Type: NumOrStringNum, NumVal: p},
)
}
// ProtocolV3FromProtocolV1 creates a v3 Protocol from a v1 Protocol,
// while handling case conversion.
func ProtocolV3FromProtocolV1(p Protocol) Protocol {
if p.Type == NumOrStringNum {
return p
}
for _, n := range allProtocolNames {
if strings.ToLower(n) == strings.ToLower(p.StrVal) {
return Protocol(
Uint8OrString{Type: NumOrStringString, StrVal: n},
)
}
}
return p
}
// ProtocolFromString creates a Protocol struct from a string value.
func ProtocolFromString(p string) Protocol {
for _, n := range allProtocolNames {
if strings.ToLower(n) == strings.ToLower(p) {
return Protocol(
Uint8OrString{Type: NumOrStringString, StrVal: n},
)
}
}
// Unknown protocol - return the value unchanged. Validation should catch this.
return Protocol(
Uint8OrString{Type: NumOrStringString, StrVal: p},
)
}
// ProtocolFromStringV1 creates a Protocol struct from a string value (for the v1 API)
func ProtocolFromStringV1(p string) Protocol {
return Protocol(
Uint8OrString{Type: NumOrStringString, StrVal: strings.ToLower(p)},
)
}
// UnmarshalJSON implements the json.Unmarshaller interface.
func (p *Protocol) UnmarshalJSON(b []byte) error {
return (*Uint8OrString)(p).UnmarshalJSON(b)
}
// MarshalJSON implements the json.Marshaller interface.
func (p Protocol) MarshalJSON() ([]byte, error) {
return Uint8OrString(p).MarshalJSON()
}
// String returns the string value, or the Itoa of the int value.
func (p Protocol) String() string {
return (Uint8OrString)(p).String()
}
// String returns the string value, or the Itoa of the int value.
func (p Protocol) ToV1() Protocol {
if p.Type == NumOrStringNum {
return p
}
return ProtocolFromStringV1(p.StrVal)
}
// NumValue returns the NumVal if type Int, or if
// it is a String, will attempt a conversion to int.
func (p Protocol) NumValue() (uint8, error) {
return (Uint8OrString)(p).NumValue()
}
// SupportsProtocols returns whether this protocol supports ports. This returns true if
// the numerical or string verion of the protocol indicates TCP (6) or UDP (17).
func (p Protocol) SupportsPorts() bool {
num, err := p.NumValue()
if err == nil {
return num == 6 || num == 17
} else {
switch p.StrVal {
case ProtocolTCP, ProtocolUDP, ProtocolTCPV1, ProtocolUDPV1:
return true
}
return false
}
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// 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 numorstring
// Type represents the stored type of Int32OrString.
type NumOrStringType int
const (
NumOrStringNum NumOrStringType = iota // The structure holds a number.
NumOrStringString // The structure holds a string.
)

View File

@@ -0,0 +1,80 @@
// Copyright (c) 2016 Tigera, Inc. All rights reserved.
// 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 numorstring
import (
"encoding/json"
"strconv"
)
// UInt8OrString is a type that can hold an uint8 or a string. When used in
// JSON or YAML marshalling and unmarshalling, it produces or consumes the
// inner type. This allows you to have, for example, a JSON field that can
// accept a name or number.
type Uint8OrString struct {
Type NumOrStringType
NumVal uint8
StrVal string
}
// UnmarshalJSON implements the json.Unmarshaller interface.
func (i *Uint8OrString) UnmarshalJSON(b []byte) error {
if b[0] == '"' {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
num, err := strconv.ParseUint(s, 10, 8)
if err == nil {
i.Type = NumOrStringNum
i.NumVal = uint8(num)
} else {
i.Type = NumOrStringString
i.StrVal = s
}
return nil
}
i.Type = NumOrStringNum
return json.Unmarshal(b, &i.NumVal)
}
// MarshalJSON implements the json.Marshaller interface.
func (i Uint8OrString) MarshalJSON() ([]byte, error) {
if num, err := i.NumValue(); err == nil {
return json.Marshal(num)
} else {
return json.Marshal(i.StrVal)
}
}
// String returns the string value, or the Itoa of the int value.
func (i Uint8OrString) String() string {
if i.Type == NumOrStringString {
return i.StrVal
}
return strconv.FormatUint(uint64(i.NumVal), 10)
}
// NumValue returns the NumVal if type Int, or if
// it is a String, will attempt a conversion to int.
func (i Uint8OrString) NumValue() (uint8, error) {
if i.Type == NumOrStringString {
num, err := strconv.ParseUint(i.StrVal, 10, 8)
return uint8(num), err
}
return i.NumVal, nil
}

View File

@@ -33,7 +33,7 @@ var c client.Client
func TestMain(m *testing.M) {
t := &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")},
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "kustomize", "network", "crds")},
}
err := SchemeBuilder.AddToScheme(scheme.Scheme)

View File

@@ -75,6 +75,7 @@ type WorkspaceNetworkPolicyStatus struct {
// WorkspaceNetworkPolicy is a set of network policies applied to the scope to workspace
// +k8s:openapi-gen=true
// +kubebuilder:resource:categories="networking",scope="Cluster",shortName="wsnp"
type WorkspaceNetworkPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

View File

@@ -16,16 +16,287 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
// autogenerated by controller-gen object, do not modify manually
package v1alpha1
import (
v1 "k8s.io/api/networking/v1"
"k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EntityRule) DeepCopyInto(out *EntityRule) {
*out = *in
if in.Nets != nil {
in, out := &in.Nets, &out.Nets
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]numorstring.Port, len(*in))
copy(*out, *in)
}
if in.NotNets != nil {
in, out := &in.NotNets, &out.NotNets
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.NotPorts != nil {
in, out := &in.NotPorts, &out.NotPorts
*out = make([]numorstring.Port, len(*in))
copy(*out, *in)
}
if in.ServiceAccounts != nil {
in, out := &in.ServiceAccounts, &out.ServiceAccounts
*out = new(ServiceAccountMatch)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EntityRule.
func (in *EntityRule) DeepCopy() *EntityRule {
if in == nil {
return nil
}
out := new(EntityRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPMatch) DeepCopyInto(out *HTTPMatch) {
*out = *in
if in.Methods != nil {
in, out := &in.Methods, &out.Methods
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Paths != nil {
in, out := &in.Paths, &out.Paths
*out = make([]HTTPPath, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMatch.
func (in *HTTPMatch) DeepCopy() *HTTPMatch {
if in == nil {
return nil
}
out := new(HTTPMatch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPPath) DeepCopyInto(out *HTTPPath) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPPath.
func (in *HTTPPath) DeepCopy() *HTTPPath {
if in == nil {
return nil
}
out := new(HTTPPath)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ICMPFields) DeepCopyInto(out *ICMPFields) {
*out = *in
if in.Type != nil {
in, out := &in.Type, &out.Type
*out = new(int)
**out = **in
}
if in.Code != nil {
in, out := &in.Code, &out.Code
*out = new(int)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ICMPFields.
func (in *ICMPFields) DeepCopy() *ICMPFields {
if in == nil {
return nil
}
out := new(ICMPFields)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamespaceNetworkPolicy) DeepCopyInto(out *NamespaceNetworkPolicy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicy.
func (in *NamespaceNetworkPolicy) DeepCopy() *NamespaceNetworkPolicy {
if in == nil {
return nil
}
out := new(NamespaceNetworkPolicy)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NamespaceNetworkPolicy) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamespaceNetworkPolicyList) DeepCopyInto(out *NamespaceNetworkPolicyList) {
*out = *in
out.TypeMeta = in.TypeMeta
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]NamespaceNetworkPolicy, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicyList.
func (in *NamespaceNetworkPolicyList) DeepCopy() *NamespaceNetworkPolicyList {
if in == nil {
return nil
}
out := new(NamespaceNetworkPolicyList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NamespaceNetworkPolicyList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NamespaceNetworkPolicySpec) DeepCopyInto(out *NamespaceNetworkPolicySpec) {
*out = *in
if in.Order != nil {
in, out := &in.Order, &out.Order
*out = new(int)
**out = **in
}
if in.Ingress != nil {
in, out := &in.Ingress, &out.Ingress
*out = make([]Rule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Egress != nil {
in, out := &in.Egress, &out.Egress
*out = make([]Rule, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Types != nil {
in, out := &in.Types, &out.Types
*out = make([]PolicyType, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceNetworkPolicySpec.
func (in *NamespaceNetworkPolicySpec) DeepCopy() *NamespaceNetworkPolicySpec {
if in == nil {
return nil
}
out := new(NamespaceNetworkPolicySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Rule) DeepCopyInto(out *Rule) {
*out = *in
if in.IPVersion != nil {
in, out := &in.IPVersion, &out.IPVersion
*out = new(int)
**out = **in
}
if in.Protocol != nil {
in, out := &in.Protocol, &out.Protocol
*out = new(v1.Protocol)
**out = **in
}
if in.ICMP != nil {
in, out := &in.ICMP, &out.ICMP
*out = new(ICMPFields)
(*in).DeepCopyInto(*out)
}
if in.NotProtocol != nil {
in, out := &in.NotProtocol, &out.NotProtocol
*out = new(v1.Protocol)
**out = **in
}
if in.NotICMP != nil {
in, out := &in.NotICMP, &out.NotICMP
*out = new(ICMPFields)
(*in).DeepCopyInto(*out)
}
in.Source.DeepCopyInto(&out.Source)
in.Destination.DeepCopyInto(&out.Destination)
if in.HTTP != nil {
in, out := &in.HTTP, &out.HTTP
*out = new(HTTPMatch)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
func (in *Rule) DeepCopy() *Rule {
if in == nil {
return nil
}
out := new(Rule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServiceAccountMatch) DeepCopyInto(out *ServiceAccountMatch) {
*out = *in
if in.Names != nil {
in, out := &in.Names, &out.Names
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountMatch.
func (in *ServiceAccountMatch) DeepCopy() *ServiceAccountMatch {
if in == nil {
return nil
}
out := new(ServiceAccountMatch)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicy) DeepCopyInto(out *WorkspaceNetworkPolicy) {
*out = *in
@@ -33,7 +304,6 @@ func (in *WorkspaceNetworkPolicy) DeepCopyInto(out *WorkspaceNetworkPolicy) {
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicy.
@@ -59,7 +329,7 @@ func (in *WorkspaceNetworkPolicyEgressRule) DeepCopyInto(out *WorkspaceNetworkPo
*out = *in
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]v1.NetworkPolicyPort, len(*in))
*out = make([]networkingv1.NetworkPolicyPort, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
@@ -71,7 +341,6 @@ func (in *WorkspaceNetworkPolicyEgressRule) DeepCopyInto(out *WorkspaceNetworkPo
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyEgressRule.
@@ -89,7 +358,7 @@ func (in *WorkspaceNetworkPolicyIngressRule) DeepCopyInto(out *WorkspaceNetworkP
*out = *in
if in.Ports != nil {
in, out := &in.Ports, &out.Ports
*out = make([]v1.NetworkPolicyPort, len(*in))
*out = make([]networkingv1.NetworkPolicyPort, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
@@ -101,7 +370,6 @@ func (in *WorkspaceNetworkPolicyIngressRule) DeepCopyInto(out *WorkspaceNetworkP
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyIngressRule.
@@ -126,7 +394,6 @@ func (in *WorkspaceNetworkPolicyList) DeepCopyInto(out *WorkspaceNetworkPolicyLi
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyList.
@@ -156,7 +423,6 @@ func (in *WorkspaceNetworkPolicyPeer) DeepCopyInto(out *WorkspaceNetworkPolicyPe
*out = new(metav1.LabelSelector)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyPeer.
@@ -174,7 +440,7 @@ func (in *WorkspaceNetworkPolicySpec) DeepCopyInto(out *WorkspaceNetworkPolicySp
*out = *in
if in.PolicyTypes != nil {
in, out := &in.PolicyTypes, &out.PolicyTypes
*out = make([]v1.PolicyType, len(*in))
*out = make([]networkingv1.PolicyType, len(*in))
copy(*out, *in)
}
if in.Ingress != nil {
@@ -191,7 +457,6 @@ func (in *WorkspaceNetworkPolicySpec) DeepCopyInto(out *WorkspaceNetworkPolicySp
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicySpec.
@@ -207,7 +472,6 @@ func (in *WorkspaceNetworkPolicySpec) DeepCopy() *WorkspaceNetworkPolicySpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceNetworkPolicyStatus) DeepCopyInto(out *WorkspaceNetworkPolicyStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyStatus.

View File

@@ -0,0 +1,128 @@
/*
Copyright 2019 The KubeSphere 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
v1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
// FakeNamespaceNetworkPolicies implements NamespaceNetworkPolicyInterface
type FakeNamespaceNetworkPolicies struct {
Fake *FakeNetworkV1alpha1
ns string
}
var namespacenetworkpoliciesResource = schema.GroupVersionResource{Group: "network.kubesphere.io", Version: "v1alpha1", Resource: "namespacenetworkpolicies"}
var namespacenetworkpoliciesKind = schema.GroupVersionKind{Group: "network.kubesphere.io", Version: "v1alpha1", Kind: "NamespaceNetworkPolicy"}
// Get takes name of the namespaceNetworkPolicy, and returns the corresponding namespaceNetworkPolicy object, and an error if there is any.
func (c *FakeNamespaceNetworkPolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(namespacenetworkpoliciesResource, c.ns, name), &v1alpha1.NamespaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), err
}
// List takes label and field selectors, and returns the list of NamespaceNetworkPolicies that match those selectors.
func (c *FakeNamespaceNetworkPolicies) List(opts v1.ListOptions) (result *v1alpha1.NamespaceNetworkPolicyList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(namespacenetworkpoliciesResource, namespacenetworkpoliciesKind, c.ns, opts), &v1alpha1.NamespaceNetworkPolicyList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v1alpha1.NamespaceNetworkPolicyList{ListMeta: obj.(*v1alpha1.NamespaceNetworkPolicyList).ListMeta}
for _, item := range obj.(*v1alpha1.NamespaceNetworkPolicyList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested namespaceNetworkPolicies.
func (c *FakeNamespaceNetworkPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(namespacenetworkpoliciesResource, c.ns, opts))
}
// Create takes the representation of a namespaceNetworkPolicy and creates it. Returns the server's representation of the namespaceNetworkPolicy, and an error, if there is any.
func (c *FakeNamespaceNetworkPolicies) Create(namespaceNetworkPolicy *v1alpha1.NamespaceNetworkPolicy) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(namespacenetworkpoliciesResource, c.ns, namespaceNetworkPolicy), &v1alpha1.NamespaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), err
}
// Update takes the representation of a namespaceNetworkPolicy and updates it. Returns the server's representation of the namespaceNetworkPolicy, and an error, if there is any.
func (c *FakeNamespaceNetworkPolicies) Update(namespaceNetworkPolicy *v1alpha1.NamespaceNetworkPolicy) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(namespacenetworkpoliciesResource, c.ns, namespaceNetworkPolicy), &v1alpha1.NamespaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), err
}
// Delete takes name of the namespaceNetworkPolicy and deletes it. Returns an error if one occurs.
func (c *FakeNamespaceNetworkPolicies) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(namespacenetworkpoliciesResource, c.ns, name), &v1alpha1.NamespaceNetworkPolicy{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeNamespaceNetworkPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(namespacenetworkpoliciesResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v1alpha1.NamespaceNetworkPolicyList{})
return err
}
// Patch applies the patch and returns the patched namespaceNetworkPolicy.
func (c *FakeNamespaceNetworkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(namespacenetworkpoliciesResource, c.ns, name, pt, data, subresources...), &v1alpha1.NamespaceNetworkPolicy{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), err
}

View File

@@ -28,6 +28,10 @@ type FakeNetworkV1alpha1 struct {
*testing.Fake
}
func (c *FakeNetworkV1alpha1) NamespaceNetworkPolicies(namespace string) v1alpha1.NamespaceNetworkPolicyInterface {
return &FakeNamespaceNetworkPolicies{c, namespace}
}
func (c *FakeNetworkV1alpha1) WorkspaceNetworkPolicies() v1alpha1.WorkspaceNetworkPolicyInterface {
return &FakeWorkspaceNetworkPolicies{c}
}

View File

@@ -18,4 +18,6 @@ limitations under the License.
package v1alpha1
type NamespaceNetworkPolicyExpansion interface{}
type WorkspaceNetworkPolicyExpansion interface{}

View File

@@ -0,0 +1,174 @@
/*
Copyright 2019 The KubeSphere 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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
"time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
v1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
)
// NamespaceNetworkPoliciesGetter has a method to return a NamespaceNetworkPolicyInterface.
// A group's client should implement this interface.
type NamespaceNetworkPoliciesGetter interface {
NamespaceNetworkPolicies(namespace string) NamespaceNetworkPolicyInterface
}
// NamespaceNetworkPolicyInterface has methods to work with NamespaceNetworkPolicy resources.
type NamespaceNetworkPolicyInterface interface {
Create(*v1alpha1.NamespaceNetworkPolicy) (*v1alpha1.NamespaceNetworkPolicy, error)
Update(*v1alpha1.NamespaceNetworkPolicy) (*v1alpha1.NamespaceNetworkPolicy, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v1alpha1.NamespaceNetworkPolicy, error)
List(opts v1.ListOptions) (*v1alpha1.NamespaceNetworkPolicyList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.NamespaceNetworkPolicy, err error)
NamespaceNetworkPolicyExpansion
}
// namespaceNetworkPolicies implements NamespaceNetworkPolicyInterface
type namespaceNetworkPolicies struct {
client rest.Interface
ns string
}
// newNamespaceNetworkPolicies returns a NamespaceNetworkPolicies
func newNamespaceNetworkPolicies(c *NetworkV1alpha1Client, namespace string) *namespaceNetworkPolicies {
return &namespaceNetworkPolicies{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the namespaceNetworkPolicy, and returns the corresponding namespaceNetworkPolicy object, and an error if there is any.
func (c *namespaceNetworkPolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
result = &v1alpha1.NamespaceNetworkPolicy{}
err = c.client.Get().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of NamespaceNetworkPolicies that match those selectors.
func (c *namespaceNetworkPolicies) List(opts v1.ListOptions) (result *v1alpha1.NamespaceNetworkPolicyList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1alpha1.NamespaceNetworkPolicyList{}
err = c.client.Get().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested namespaceNetworkPolicies.
func (c *namespaceNetworkPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a namespaceNetworkPolicy and creates it. Returns the server's representation of the namespaceNetworkPolicy, and an error, if there is any.
func (c *namespaceNetworkPolicies) Create(namespaceNetworkPolicy *v1alpha1.NamespaceNetworkPolicy) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
result = &v1alpha1.NamespaceNetworkPolicy{}
err = c.client.Post().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
Body(namespaceNetworkPolicy).
Do().
Into(result)
return
}
// Update takes the representation of a namespaceNetworkPolicy and updates it. Returns the server's representation of the namespaceNetworkPolicy, and an error, if there is any.
func (c *namespaceNetworkPolicies) Update(namespaceNetworkPolicy *v1alpha1.NamespaceNetworkPolicy) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
result = &v1alpha1.NamespaceNetworkPolicy{}
err = c.client.Put().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
Name(namespaceNetworkPolicy.Name).
Body(namespaceNetworkPolicy).
Do().
Into(result)
return
}
// Delete takes name of the namespaceNetworkPolicy and deletes it. Returns an error if one occurs.
func (c *namespaceNetworkPolicies) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *namespaceNetworkPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("namespacenetworkpolicies").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched namespaceNetworkPolicy.
func (c *namespaceNetworkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.NamespaceNetworkPolicy, err error) {
result = &v1alpha1.NamespaceNetworkPolicy{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("namespacenetworkpolicies").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@@ -27,6 +27,7 @@ import (
type NetworkV1alpha1Interface interface {
RESTClient() rest.Interface
NamespaceNetworkPoliciesGetter
WorkspaceNetworkPoliciesGetter
}
@@ -35,6 +36,10 @@ type NetworkV1alpha1Client struct {
restClient rest.Interface
}
func (c *NetworkV1alpha1Client) NamespaceNetworkPolicies(namespace string) NamespaceNetworkPolicyInterface {
return newNamespaceNetworkPolicies(c, namespace)
}
func (c *NetworkV1alpha1Client) WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInterface {
return newWorkspaceNetworkPolicies(c)
}

View File

@@ -60,6 +60,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iBinaries().Informer()}, nil
// Group=network.kubesphere.io, Version=v1alpha1
case networkv1alpha1.SchemeGroupVersion.WithResource("namespacenetworkpolicies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().NamespaceNetworkPolicies().Informer()}, nil
case networkv1alpha1.SchemeGroupVersion.WithResource("workspacenetworkpolicies"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().WorkspaceNetworkPolicies().Informer()}, nil

View File

@@ -24,6 +24,8 @@ import (
// Interface provides access to all the informers in this group version.
type Interface interface {
// NamespaceNetworkPolicies returns a NamespaceNetworkPolicyInformer.
NamespaceNetworkPolicies() NamespaceNetworkPolicyInformer
// WorkspaceNetworkPolicies returns a WorkspaceNetworkPolicyInformer.
WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInformer
}
@@ -39,6 +41,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// NamespaceNetworkPolicies returns a NamespaceNetworkPolicyInformer.
func (v *version) NamespaceNetworkPolicies() NamespaceNetworkPolicyInformer {
return &namespaceNetworkPolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// WorkspaceNetworkPolicies returns a WorkspaceNetworkPolicyInformer.
func (v *version) WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInformer {
return &workspaceNetworkPolicyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions}

View File

@@ -0,0 +1,89 @@
/*
Copyright 2019 The KubeSphere 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.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v1alpha1
import (
time "time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces"
v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
)
// NamespaceNetworkPolicyInformer provides access to a shared informer and lister for
// NamespaceNetworkPolicies.
type NamespaceNetworkPolicyInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.NamespaceNetworkPolicyLister
}
type namespaceNetworkPolicyInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewNamespaceNetworkPolicyInformer constructs a new informer for NamespaceNetworkPolicy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewNamespaceNetworkPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredNamespaceNetworkPolicyInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredNamespaceNetworkPolicyInformer constructs a new informer for NamespaceNetworkPolicy type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredNamespaceNetworkPolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.NetworkV1alpha1().NamespaceNetworkPolicies(namespace).List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.NetworkV1alpha1().NamespaceNetworkPolicies(namespace).Watch(options)
},
},
&networkv1alpha1.NamespaceNetworkPolicy{},
resyncPeriod,
indexers,
)
}
func (f *namespaceNetworkPolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredNamespaceNetworkPolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *namespaceNetworkPolicyInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&networkv1alpha1.NamespaceNetworkPolicy{}, f.defaultInformer)
}
func (f *namespaceNetworkPolicyInformer) Lister() v1alpha1.NamespaceNetworkPolicyLister {
return v1alpha1.NewNamespaceNetworkPolicyLister(f.Informer().GetIndexer())
}

View File

@@ -18,6 +18,14 @@ limitations under the License.
package v1alpha1
// NamespaceNetworkPolicyListerExpansion allows custom methods to be added to
// NamespaceNetworkPolicyLister.
type NamespaceNetworkPolicyListerExpansion interface{}
// NamespaceNetworkPolicyNamespaceListerExpansion allows custom methods to be added to
// NamespaceNetworkPolicyNamespaceLister.
type NamespaceNetworkPolicyNamespaceListerExpansion interface{}
// WorkspaceNetworkPolicyListerExpansion allows custom methods to be added to
// WorkspaceNetworkPolicyLister.
type WorkspaceNetworkPolicyListerExpansion interface{}

View File

@@ -0,0 +1,94 @@
/*
Copyright 2019 The KubeSphere 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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1alpha1
import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
v1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
// NamespaceNetworkPolicyLister helps list NamespaceNetworkPolicies.
type NamespaceNetworkPolicyLister interface {
// List lists all NamespaceNetworkPolicies in the indexer.
List(selector labels.Selector) (ret []*v1alpha1.NamespaceNetworkPolicy, err error)
// NamespaceNetworkPolicies returns an object that can list and get NamespaceNetworkPolicies.
NamespaceNetworkPolicies(namespace string) NamespaceNetworkPolicyNamespaceLister
NamespaceNetworkPolicyListerExpansion
}
// namespaceNetworkPolicyLister implements the NamespaceNetworkPolicyLister interface.
type namespaceNetworkPolicyLister struct {
indexer cache.Indexer
}
// NewNamespaceNetworkPolicyLister returns a new NamespaceNetworkPolicyLister.
func NewNamespaceNetworkPolicyLister(indexer cache.Indexer) NamespaceNetworkPolicyLister {
return &namespaceNetworkPolicyLister{indexer: indexer}
}
// List lists all NamespaceNetworkPolicies in the indexer.
func (s *namespaceNetworkPolicyLister) List(selector labels.Selector) (ret []*v1alpha1.NamespaceNetworkPolicy, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.NamespaceNetworkPolicy))
})
return ret, err
}
// NamespaceNetworkPolicies returns an object that can list and get NamespaceNetworkPolicies.
func (s *namespaceNetworkPolicyLister) NamespaceNetworkPolicies(namespace string) NamespaceNetworkPolicyNamespaceLister {
return namespaceNetworkPolicyNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// NamespaceNetworkPolicyNamespaceLister helps list and get NamespaceNetworkPolicies.
type NamespaceNetworkPolicyNamespaceLister interface {
// List lists all NamespaceNetworkPolicies in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1alpha1.NamespaceNetworkPolicy, err error)
// Get retrieves the NamespaceNetworkPolicy from the indexer for a given namespace and name.
Get(name string) (*v1alpha1.NamespaceNetworkPolicy, error)
NamespaceNetworkPolicyNamespaceListerExpansion
}
// namespaceNetworkPolicyNamespaceLister implements the NamespaceNetworkPolicyNamespaceLister
// interface.
type namespaceNetworkPolicyNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all NamespaceNetworkPolicies in the indexer for a given namespace.
func (s namespaceNetworkPolicyNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.NamespaceNetworkPolicy, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1alpha1.NamespaceNetworkPolicy))
})
return ret, err
}
// Get retrieves the NamespaceNetworkPolicy from the indexer for a given namespace and name.
func (s namespaceNetworkPolicyNamespaceLister) Get(name string) (*v1alpha1.NamespaceNetworkPolicy, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1alpha1.Resource("namespacenetworkpolicy"), name)
}
return obj.(*v1alpha1.NamespaceNetworkPolicy), nil
}

View File

@@ -0,0 +1,6 @@
package controllerapi
// Controller expose Run method
type Controller interface {
Run(threadiness int, stopCh <-chan struct{}) error
}

View File

@@ -0,0 +1,5 @@
package network
// +kubebuilder:rbac:groups=network.kubesphere.io,resources=workspacenetworkpolicies;namespacenetworkpolicies,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups:core,resource=namespaces,verbs=get;list;watch;create;update;patch
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch;create;update;patch;delete

View File

@@ -0,0 +1,177 @@
package nsnetworkpolicy
import (
"fmt"
"time"
corev1 "k8s.io/api/core/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog"
"k8s.io/klog/klogr"
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
networkinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1"
networklister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
"kubesphere.io/kubesphere/pkg/controller/network/provider"
)
const controllerAgentName = "nsnp-controller"
type controller struct {
kubeClientset kubernetes.Interface
kubesphereClientset kubesphereclient.Interface
nsnpInformer networkinformer.NamespaceNetworkPolicyInformer
nsnpLister networklister.NamespaceNetworkPolicyLister
nsnpSynced cache.InformerSynced
// workqueue is a rate limited work queue. This is used to queue work to be
// processed instead of performing it as soon as a change happens. This
// means we can ensure we only process a fixed amount of resources at a
// time, and makes it easy to ensure we are never processing the same item
// simultaneously in two different workers.
workqueue workqueue.RateLimitingInterface
// recorder is an event recorder for recording Event resources to the
// Kubernetes API.
recorder record.EventRecorder
nsNetworkPolicyProvider provider.NsNetworkPolicyProvider
}
var (
log = klogr.New().WithName("Controller").WithValues("Component", controllerAgentName)
errCount = 0
)
func NewController(kubeclientset kubernetes.Interface,
kubesphereclientset kubesphereclient.Interface,
nsnpInformer networkinformer.NamespaceNetworkPolicyInformer,
nsNetworkPolicyProvider provider.NsNetworkPolicyProvider) controllerapi.Controller {
utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme))
log.V(4).Info("Creating event broadcaster")
eventBroadcaster := record.NewBroadcaster()
eventBroadcaster.StartLogging(klog.Infof)
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")})
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName})
ctl := &controller{
kubeClientset: kubeclientset,
kubesphereClientset: kubesphereclientset,
nsnpInformer: nsnpInformer,
nsnpLister: nsnpInformer.Lister(),
nsnpSynced: nsnpInformer.Informer().HasSynced,
nsNetworkPolicyProvider: nsNetworkPolicyProvider,
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "NamespaceNetworkPolicies"),
recorder: recorder,
}
log.Info("Setting up event handlers")
nsnpInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: ctl.enqueueNSNP,
UpdateFunc: func(old, new interface{}) {
ctl.enqueueNSNP(new)
},
DeleteFunc: ctl.enqueueNSNP,
})
return ctl
}
func (c *controller) Run(threadiness int, stopCh <-chan struct{}) error {
defer utilruntime.HandleCrash()
defer c.workqueue.ShutDown()
//init client
// Start the informer factories to begin populating the informer caches
log.V(1).Info("Starting WSNP controller")
// Wait for the caches to be synced before starting workers
log.V(2).Info("Waiting for informer caches to sync")
if ok := cache.WaitForCacheSync(stopCh, c.nsnpSynced); !ok {
return fmt.Errorf("failed to wait for caches to sync")
}
log.Info("Starting workers")
// Launch two workers to process Foo resources
for i := 0; i < threadiness; i++ {
go wait.Until(c.runWorker, time.Second, stopCh)
}
klog.V(2).Info("Started workers")
<-stopCh
log.V(2).Info("Shutting down workers")
return nil
}
func (c *controller) enqueueNSNP(obj interface{}) {
var key string
var err error
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
utilruntime.HandleError(err)
return
}
c.workqueue.Add(key)
}
func (c *controller) runWorker() {
for c.processNextWorkItem() {
}
}
func (c *controller) processNextWorkItem() bool {
obj, shutdown := c.workqueue.Get()
if shutdown {
return false
}
// We wrap this block in a func so we can defer c.workqueue.Done.
err := func(obj interface{}) error {
// We call Done here so the workqueue knows we have finished
// processing this item. We also must remember to call Forget if we
// do not want this work item being re-queued. For example, we do
// not call Forget if a transient error occurs, instead the item is
// put back on the workqueue and attempted again after a back-off
// period.
defer c.workqueue.Done(obj)
var key string
var ok bool
// We expect strings to come off the workqueue. These are of the
// form namespace/name. We do this as the delayed nature of the
// workqueue means the items in the informer cache may actually be
// more up to date that when the item was initially put onto the
// workqueue.
if key, ok = obj.(string); !ok {
// As the item in the workqueue is actually invalid, we call
// Forget here else we'd go into a loop of attempting to
// process a work item that is invalid.
c.workqueue.Forget(obj)
utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj))
return nil
}
// Run the reconcile, passing it the namespace/name string of the
// Foo resource to be synced.
if err := c.reconcile(key); err != nil {
// Put the item back on the workqueue to handle any transient errors.
c.workqueue.AddRateLimited(key)
return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error())
}
// Finally, if no error occurs we Forget this item so it does not
// get queued again until another change happens.
c.workqueue.Forget(obj)
log.Info("Successfully synced", "key", key)
return nil
}(obj)
if err != nil {
utilruntime.HandleError(err)
return true
}
return true
}

View File

@@ -0,0 +1,21 @@
package nsnetworkpolicy
import (
"flag"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/klog"
)
func TestNsnetworkpolicy(t *testing.T) {
klog.InitFlags(nil)
flag.Set("logtostderr", "false")
flag.Set("alsologtostderr", "false")
flag.Set("v", "4")
flag.Parse()
klog.SetOutput(GinkgoWriter)
RegisterFailHandler(Fail)
RunSpecs(t, "Nsnetworkpolicy Suite")
}

View File

@@ -0,0 +1,93 @@
package nsnetworkpolicy
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/record"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
nsnplister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
"kubesphere.io/kubesphere/pkg/controller/network/provider"
controllertesting "kubesphere.io/kubesphere/pkg/controller/network/testing"
)
var (
fakeControllerBuilder *controllertesting.FakeControllerBuilder
c controllerapi.Controller
stopCh chan struct{}
calicoProvider *provider.FakeCalicoNetworkProvider
nsnpLister nsnplister.NamespaceNetworkPolicyLister
)
var _ = Describe("Nsnetworkpolicy", func() {
BeforeEach(func() {
fakeControllerBuilder = controllertesting.NewFakeControllerBuilder()
stopCh = make(chan struct{})
informer, _ := fakeControllerBuilder.NewControllerInformer()
calicoProvider = provider.NewFakeCalicoNetworkProvider()
c = NewController(fakeControllerBuilder.KubeClient, fakeControllerBuilder.KsClient, informer.Network().V1alpha1().NamespaceNetworkPolicies(), calicoProvider)
go informer.Network().V1alpha1().NamespaceNetworkPolicies().Informer().Run(stopCh)
originalController := c.(*controller)
originalController.recorder = &record.FakeRecorder{}
go c.Run(1, stopCh)
nsnpLister = informer.Network().V1alpha1().NamespaceNetworkPolicies().Lister()
})
It("Should create a new calico object", func() {
objSrt := `{
"apiVersion": "network.kubesphere.io/v1alpha1",
"kind": "NetworkPolicy",
"metadata": {
"name": "allow-tcp-6379",
"namespace": "production"
},
"spec": {
"selector": "color == 'red'",
"ingress": [
{
"action": "Allow",
"protocol": "TCP",
"source": {
"selector": "color == 'blue'"
},
"destination": {
"ports": [
6379
]
}
}
]
}
}`
obj := &v1alpha1.NamespaceNetworkPolicy{}
Expect(controllertesting.StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
_, err := fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Create(obj)
Expect(err).ShouldNot(HaveOccurred())
Eventually(func() bool {
exist, _ := calicoProvider.CheckExist(obj)
return exist
}).Should(BeTrue())
obj, _ = fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Get(obj.Name, metav1.GetOptions{})
Expect(obj.Finalizers).To(HaveLen(1))
// TestUpdate
newStr := "color == 'green'"
obj.Spec.Selector = newStr
_, err = fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj)
Expect(err).ShouldNot(HaveOccurred())
Eventually(func() string {
o, err := calicoProvider.Get(obj)
if err != nil {
return err.Error()
}
n := o.(*v1alpha1.NamespaceNetworkPolicy)
return n.Spec.Selector
}).Should(Equal(newStr))
// TestDelete
Expect(fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Delete(obj.Name, &metav1.DeleteOptions{})).ShouldNot(HaveOccurred())
})
AfterEach(func() {
close(stopCh)
})
})

View File

@@ -0,0 +1,119 @@
package nsnetworkpolicy
import (
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/retry"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/utils"
)
const (
controllerFinalizier = "nsnp.finalizers.networking.kubesphere.io"
)
var clog logr.Logger
func (c *controller) reconcile(key string) error {
namespace, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
return err
}
clog = log.WithValues("name", name, "namespace", namespace)
clog.V(1).Info("---------Begin to reconcile--------")
defer clog.V(1).Info("---------Reconcile done--------")
obj, err := c.nsnpLister.NamespaceNetworkPolicies(namespace).Get(name)
if err != nil {
if errors.IsNotFound(err) {
clog.V(2).Info("Object is removed")
return nil
}
clog.Error(err, "Failed to get resource")
return err
}
stop, err := c.addOrRemoveFinalizer(obj)
if err != nil {
return err
}
if stop {
return nil
}
clog.V(2).Info("Check if we need a create or update")
ok, err := c.nsNetworkPolicyProvider.CheckExist(obj)
if err != nil {
clog.Error(err, "Failed to check exist of network policy")
return err
}
if !ok {
clog.V(1).Info("Create a new object in backend")
err = c.nsNetworkPolicyProvider.Add(obj)
if err != nil {
clog.Error(err, "Failed to create np")
return err
}
return nil
}
needUpdate, err := c.nsNetworkPolicyProvider.NeedUpdate(obj)
if err != nil {
clog.Error(err, "Failed to check if object need a update")
return err
}
if needUpdate {
clog.V(1).Info("Update object in backend")
err = c.nsNetworkPolicyProvider.Update(obj)
if err != nil {
clog.Error(err, "Failed to update object")
return err
}
}
return nil
}
func (c *controller) addOrRemoveFinalizer(obj *v1alpha1.NamespaceNetworkPolicy) (bool, error) {
if obj.ObjectMeta.DeletionTimestamp.IsZero() {
if !utils.ContainsString(obj.ObjectMeta.Finalizers, controllerFinalizier) {
clog.V(2).Info("Detect no finalizer")
obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, controllerFinalizier)
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
_, err := c.kubesphereClientset.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj)
return err
})
if err != nil {
clog.Error(err, "Failed to add finalizer")
return false, err
}
return false, nil
}
} else {
// The object is being deleted
if utils.ContainsString(obj.ObjectMeta.Finalizers, controllerFinalizier) {
// our finalizer is present, so lets handle any external dependency
if err := c.deleteProviderNSNP(obj); err != nil {
// if fail to delete the external dependency here, return with error
// so that it can be retried
return false, err
}
clog.V(2).Info("Removing finalizer")
// remove our finalizer from the list and update it.
obj.ObjectMeta.Finalizers = utils.RemoveString(obj.ObjectMeta.Finalizers, controllerFinalizier)
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
_, err := c.kubesphereClientset.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj)
return err
})
if err != nil {
clog.Error(err, "Failed to remove finalizer")
return false, err
}
return true, nil
}
}
return false, nil
}
// deleteProviderNSNP delete network policy in the backend
func (c *controller) deleteProviderNSNP(obj *v1alpha1.NamespaceNetworkPolicy) error {
clog.V(2).Info("Deleting backend network policy")
return c.nsNetworkPolicyProvider.Delete(obj)
}

View File

@@ -0,0 +1,66 @@
package provider
import (
"reflect"
"github.com/projectcalico/libcalico-go/lib/errors"
"k8s.io/client-go/tools/cache"
api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
func NewFakeCalicoNetworkProvider() *FakeCalicoNetworkProvider {
f := new(FakeCalicoNetworkProvider)
f.NSNPData = make(map[string]*api.NamespaceNetworkPolicy)
return f
}
type FakeCalicoNetworkProvider struct {
NSNPData map[string]*api.NamespaceNetworkPolicy
}
func (f *FakeCalicoNetworkProvider) Get(o *api.NamespaceNetworkPolicy) (interface{}, error) {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
obj, ok := f.NSNPData[namespacename]
if !ok {
return nil, errors.ErrorResourceDoesNotExist{}
}
return obj, nil
}
func (f *FakeCalicoNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
if _, ok := f.NSNPData[namespacename]; ok {
return errors.ErrorResourceAlreadyExists{}
}
f.NSNPData[namespacename] = o
return nil
}
func (f *FakeCalicoNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
if _, ok := f.NSNPData[namespacename]; ok {
return true, nil
}
return false, nil
}
func (f *FakeCalicoNetworkProvider) NeedUpdate(o *api.NamespaceNetworkPolicy) (bool, error) {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
store := f.NSNPData[namespacename]
if !reflect.DeepEqual(store, o) {
return true, nil
}
return false, nil
}
func (f *FakeCalicoNetworkProvider) Update(o *api.NamespaceNetworkPolicy) error {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
f.NSNPData[namespacename] = o
return nil
}
func (f *FakeCalicoNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error {
namespacename, _ := cache.MetaNamespaceKeyFunc(o)
delete(f.NSNPData, namespacename)
return nil
}

View File

@@ -0,0 +1 @@
package provider

View File

@@ -0,0 +1,35 @@
package provider
import (
k8snetworkinformer "k8s.io/client-go/informers/networking/v1"
k8snetworklister "k8s.io/client-go/listers/networking/v1"
api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
// NsNetworkPolicyProvider is a interface to let different cnis to implement our api
type NsNetworkPolicyProvider interface {
Add(*api.NamespaceNetworkPolicy) error
CheckExist(*api.NamespaceNetworkPolicy) (bool, error)
NeedUpdate(*api.NamespaceNetworkPolicy) (bool, error)
Update(*api.NamespaceNetworkPolicy) error
Delete(*api.NamespaceNetworkPolicy) error
Get(*api.NamespaceNetworkPolicy) (interface{}, error)
}
// TODO: support no-calico CNI
type k8sNetworkProvider struct {
networkPolicyInformer k8snetworkinformer.NetworkPolicyInformer
networkPolicyLister k8snetworklister.NetworkPolicyLister
}
func (k *k8sNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error {
return nil
}
func (k *k8sNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) {
return false, nil
}
func (k *k8sNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error {
return nil
}

View File

@@ -0,0 +1,144 @@
package provider
import (
"context"
"encoding/json"
"reflect"
"time"
v3 "github.com/projectcalico/libcalico-go/lib/apis/v3"
"github.com/projectcalico/libcalico-go/lib/clientv3"
"github.com/projectcalico/libcalico-go/lib/errors"
"github.com/projectcalico/libcalico-go/lib/options"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/klog/klogr"
api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
var log = klogr.New().WithName("calico-client")
var defaultBackoff = wait.Backoff{
Steps: 4,
Duration: 10 * time.Millisecond,
Factor: 5.0,
Jitter: 0.1,
}
type calicoNetworkProvider struct {
np clientv3.NetworkPolicyInterface
}
func NewCalicoNetworkProvider(np clientv3.NetworkPolicyInterface) NsNetworkPolicyProvider {
return &calicoNetworkProvider{
np: np,
}
}
func convertSpec(n *api.NamespaceNetworkPolicySpec) *v3.NetworkPolicySpec {
bytes, err := json.Marshal(&n)
if err != nil {
panic(err)
}
m := new(v3.NetworkPolicySpec)
err = json.Unmarshal(bytes, m)
if err != nil {
panic(err)
}
return m
}
// ConvertAPIToCalico convert our api to calico api
func ConvertAPIToCalico(n *api.NamespaceNetworkPolicy) *v3.NetworkPolicy {
output := v3.NewNetworkPolicy()
//Object Metadata
output.ObjectMeta.Name = n.Name
output.Namespace = n.Namespace
output.Annotations = n.Annotations
output.Labels = n.Labels
//spec
output.Spec = *(convertSpec(&n.Spec))
return output
}
func (k *calicoNetworkProvider) Get(o *api.NamespaceNetworkPolicy) (interface{}, error) {
return k.np.Get(context.TODO(), o.Namespace, o.Name, options.GetOptions{})
}
func (k *calicoNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error {
log.V(3).Info("Creating network policy", "name", o.Name, "namespace", o.Namespace)
obj := ConvertAPIToCalico(o)
log.V(4).Info("Show object spe detail", "name", o.Name, "namespace", o.Namespace, "Spec", obj.Spec)
_, err := k.np.Create(context.TODO(), obj, options.SetOptions{})
return err
}
func (k *calicoNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) {
log.V(3).Info("Checking network policy whether exsits or not", "name", o.Name, "namespace", o.Namespace)
out, err := k.np.Get(context.Background(), o.Namespace, o.Name, options.GetOptions{})
if err != nil {
if _, ok := err.(errors.ErrorResourceDoesNotExist); ok {
return false, nil
}
return false, err
}
if out != nil {
return true, nil
}
return false, nil
}
func (k *calicoNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error {
log.V(3).Info("Deleting network policy", "name", o.Name, "namespace", o.Namespace)
_, err := k.np.Delete(context.Background(), o.Namespace, o.Name, options.DeleteOptions{})
return err
}
func (k *calicoNetworkProvider) NeedUpdate(o *api.NamespaceNetworkPolicy) (bool, error) {
store, err := k.np.Get(context.Background(), o.Namespace, o.Name, options.GetOptions{})
if err != nil {
log.Error(err, "Failed to get resource", "name", o.Name, "namespace", o.Namespace)
}
expected := ConvertAPIToCalico(o)
log.V(4).Info("Comparing Spec", "store", store.Spec, "current", expected.Spec)
if !reflect.DeepEqual(store.Spec, expected.Spec) {
return true, nil
}
return false, nil
}
func (k *calicoNetworkProvider) Update(o *api.NamespaceNetworkPolicy) error {
log.V(3).Info("Updating network policy", "name", o.Name, "namespace", o.Namespace)
updateObject, err := k.Get(o)
if err != nil {
log.Error(err, "Failed to get resource in store")
return err
}
up := updateObject.(*v3.NetworkPolicy)
up.Spec = *convertSpec(&o.Spec)
err = RetryOnConflict(defaultBackoff, func() error {
_, err := k.np.Update(context.Background(), up, options.SetOptions{})
return err
})
if err != nil {
log.Error(err, "Failed to update resource", "name", o.Name, "namespace", o.Namespace)
}
return err
}
// RetryOnConflict is same as the function in k8s, but replaced with error in calico
func RetryOnConflict(backoff wait.Backoff, fn func() error) error {
var lastConflictErr error
err := wait.ExponentialBackoff(backoff, func() (bool, error) {
err := fn()
if err == nil {
return true, nil
}
if _, ok := err.(errors.ErrorResourceUpdateConflict); ok {
lastConflictErr = err
return false, nil
}
return false, err
})
if err == wait.ErrWaitTimeout {
err = lastConflictErr
}
return err
}

View File

@@ -0,0 +1,82 @@
package runoption
import (
"time"
"github.com/projectcalico/libcalico-go/lib/apiconfig"
"github.com/projectcalico/libcalico-go/lib/clientv3"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned"
ksinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy"
"kubesphere.io/kubesphere/pkg/controller/network/provider"
)
type CalicoDataStoreType string
const (
certPath = "/calicocerts"
KubernetesDataStore CalicoDataStoreType = "k8s"
EtcdDataStore CalicoDataStoreType = "etcd"
)
type RunOption struct {
ProviderName string
DataStoreType CalicoDataStoreType
EtcdEndpoints string
AllowInsecureEtcd bool
}
func (r RunOption) Run() error {
klog.V(1).Info("Check config")
if err := r.check(); err != nil {
return err
}
klog.V(1).Info("Preparing kubernetes client")
config, err := rest.InClusterConfig()
if err != nil {
panic(err.Error())
}
// creates the clientset
k8sClientset := kubernetes.NewForConfigOrDie(config)
ksClientset := versioned.NewForConfigOrDie(config)
informer := ksinformer.NewSharedInformerFactory(ksClientset, time.Minute*10)
klog.V(1).Info("Kubernetes client initialized successfully")
var npProvider provider.NsNetworkPolicyProvider
if r.ProviderName == "calico" {
klog.V(1).Info("Preparing calico client")
config := apiconfig.NewCalicoAPIConfig()
config.Spec.EtcdEndpoints = r.EtcdEndpoints
if !r.AllowInsecureEtcd {
config.Spec.EtcdKeyFile = certPath + "/etcd-key"
config.Spec.EtcdCertFile = certPath + "/etcd-cert"
config.Spec.EtcdCACertFile = certPath + "/etcd-ca"
}
if r.DataStoreType == KubernetesDataStore {
config.Spec.DatastoreType = apiconfig.Kubernetes
} else {
config.Spec.DatastoreType = apiconfig.EtcdV3
}
client, err := clientv3.New(*config)
if err != nil {
klog.Fatal("Failed to initialize calico client", err)
}
npProvider = provider.NewCalicoNetworkProvider(client.NetworkPolicies())
klog.V(1).Info("Calico client initialized successfully")
}
//TODO: support no-calico cni
c := nsnetworkpolicy.NewController(k8sClientset, ksClientset, informer.Network().V1alpha1().NamespaceNetworkPolicies(), npProvider)
stop := make(chan struct{})
klog.V(1).Infof("Starting controller")
go informer.Network().V1alpha1().NamespaceNetworkPolicies().Informer().Run(stop)
return c.Run(1, stop)
}
func (r RunOption) check() error {
return nil
}

View File

@@ -0,0 +1,22 @@
package utils
// ContainsString report if s is in a slice
func ContainsString(slice []string, s string) bool {
for _, item := range slice {
if item == s {
return true
}
}
return false
}
// RemoveString remove s from slice if exists
func RemoveString(slice []string, s string) (result []string) {
for _, item := range slice {
if item == s {
continue
}
result = append(result, item)
}
return
}

View File

@@ -29,6 +29,7 @@ import (
workspaceinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
networklister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
workspacelister "kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
)
const controllerAgentName = "wsnp-controller"
@@ -38,10 +39,6 @@ var (
errCount = 0
)
// Controller expose Run method
type Controller interface {
Run(threadiness int, stopCh <-chan struct{}) error
}
type controller struct {
kubeClientset kubernetes.Interface
kubesphereClientset kubesphereclient.Interface
@@ -77,7 +74,7 @@ func NewController(kubeclientset kubernetes.Interface,
wsnpInformer networkinformer.WorkspaceNetworkPolicyInformer,
networkPolicyInformer k8snetworkinformer.NetworkPolicyInformer,
namespaceInformer corev1informer.NamespaceInformer,
workspaceInformer workspaceinformer.WorkspaceInformer) Controller {
workspaceInformer workspaceinformer.WorkspaceInformer) controllerapi.Controller {
utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme))
log.V(4).Info("Creating event broadcaster")
eventBroadcaster := record.NewBroadcaster()

View File

@@ -16,12 +16,13 @@ import (
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
tenant "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
controllertesting "kubesphere.io/kubesphere/pkg/controller/network/testing"
)
var (
fakeControllerBuilder *controllertesting.FakeControllerBuilder
c Controller
c controllerapi.Controller
npLister netv1lister.NetworkPolicyLister
stopCh chan struct{}
deletePolicy metav1.DeletionPropagation

View File

@@ -467,6 +467,8 @@ func GetUserInfo(username string) (*models.User, error) {
return nil, err
}
defer conn.Close()
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
@@ -1162,6 +1164,8 @@ func DescribeGroup(path string) (*models.Group, error) {
return nil, err
}
defer conn.Close()
groupSearchRequest := ldap.NewSearchRequest(searchBase,
ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=posixGroup)(cn=%s))", cn),

291
pkg/test/testing.go Normal file
View File

@@ -0,0 +1,291 @@
// Copyright 2018 The Kubesphere 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 test
import (
"context"
"fmt"
"io"
"io/ioutil"
"strconv"
"strings"
"testing"
"time"
"k8s.io/klog"
"github.com/prometheus/common/log"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
extscheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)
type TestCtx struct {
Client client.Client
ID string
t *testing.T
cleanupFns []cleanupFn
Namespace string
}
type CleanupOptions struct {
TestContext *TestCtx
Timeout time.Duration
RetryInterval time.Duration
}
type cleanupFn func(option *CleanupOptions) error
type AddToSchemeFunc = func(*runtime.Scheme) error
func NewTestCtx(t *testing.T, namespace string) *TestCtx {
var prefix string
if t != nil {
// TestCtx is used among others for namespace names where '/' is forbidden
prefix = strings.TrimPrefix(
strings.Replace(
strings.ToLower(t.Name()),
"/",
"-",
-1,
),
"test",
)
} else {
prefix = "main"
}
id := prefix + "-" + strconv.FormatInt(time.Now().Unix(), 10)
return &TestCtx{
ID: id,
t: t,
Namespace: namespace,
}
}
func (t *TestCtx) Setup(yamlPath string, crdPath string, schemes ...AddToSchemeFunc) error {
cfg, err := config.GetConfig()
if err != nil {
klog.Error("Failed to get kubeconfig")
return err
}
for _, f := range schemes {
f(scheme.Scheme)
}
extscheme.AddToScheme(scheme.Scheme)
dynClient, err := client.New(cfg, client.Options{})
if err != nil {
return err
}
t.Client = dynClient
err = EnsureNamespace(t.Client, t.Namespace)
if err != nil {
return err
}
bytes, err := ioutil.ReadFile(yamlPath)
if err != nil {
klog.Errorln("Failed to read yaml file")
return err
}
err = t.CreateFromYAML(bytes, true)
if err != nil {
klog.Error("Failed to install controller")
return err
}
return nil
}
func WaitForController(c client.Client, namespace, name string, replica int32, retryInterval, timeout time.Duration) error {
err := wait.Poll(retryInterval, timeout, func() (done bool, err error) {
controller := &appsv1.Deployment{}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
err = c.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, controller)
if apierrors.IsNotFound(err) {
klog.Infof("Cannot find controller %s", name)
return false, nil
}
if err != nil {
klog.Errorf("Get error %s when waiting for controller up", err.Error())
return false, err
}
if controller.Status.ReadyReplicas == replica {
return true, nil
}
return false, nil
})
return err
}
func WaitForDeletion(dynclient client.Client, obj runtime.Object, retryInterval, timeout time.Duration) error {
key, err := client.ObjectKeyFromObject(obj)
if err != nil {
return err
}
kind := obj.GetObjectKind().GroupVersionKind().Kind
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
err = wait.Poll(retryInterval, timeout, func() (done bool, err error) {
err = dynclient.Get(ctx, key, obj)
if apierrors.IsNotFound(err) {
return true, nil
}
if err != nil {
klog.Errorf("Get error %s when waiting for controller down", err.Error())
return false, err
}
klog.Infof("Waiting for %s %s to be deleted\n", kind, key)
return false, nil
})
if err != nil {
return err
}
return nil
}
func GetLogOfPod(rest *rest.RESTClient, namespace, name string, logOptions *corev1.PodLogOptions, out io.Writer) error {
req := rest.Get().Namespace(namespace).Name(name).SubResource("log").Param("follow", strconv.FormatBool(logOptions.Follow)).
Param("container", logOptions.Container).
Param("previous", strconv.FormatBool(logOptions.Previous)).
Param("timestamps", strconv.FormatBool(logOptions.Timestamps))
if logOptions.SinceSeconds != nil {
req.Param("sinceSeconds", strconv.FormatInt(*logOptions.SinceSeconds, 10))
}
if logOptions.SinceTime != nil {
req.Param("sinceTime", logOptions.SinceTime.Format(time.RFC3339))
}
if logOptions.LimitBytes != nil {
req.Param("limitBytes", strconv.FormatInt(*logOptions.LimitBytes, 10))
}
if logOptions.TailLines != nil {
req.Param("tailLines", strconv.FormatInt(*logOptions.TailLines, 10))
}
readCloser, err := req.Stream()
if err != nil {
return err
}
defer readCloser.Close()
_, err = io.Copy(out, readCloser)
return err
}
func (ctx *TestCtx) CreateFromYAML(yamlFile []byte, skipIfExists bool) error {
decode := scheme.Codecs.UniversalDeserializer().Decode
scanner := NewYAMLScanner(yamlFile)
for scanner.Scan() {
yamlSpec := scanner.Bytes()
obj, groupVersionKind, err := decode(yamlSpec, nil, nil)
if err != nil {
klog.Errorf("Error while decoding YAML object. Err was: %s", err)
return err
}
klog.Infof("Successfully decode object %v", groupVersionKind)
err = ctx.Client.Create(context.TODO(), obj)
if skipIfExists && apierrors.IsAlreadyExists(err) {
continue
}
if err != nil {
klog.Errorf("Failed to create %v to k8s", obj)
return err
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to scan manifest: (%v)", err)
}
return nil
}
func (ctx *TestCtx) GetID() string {
return ctx.ID
}
func (ctx *TestCtx) Cleanup(option *CleanupOptions) {
failed := false
for i := len(ctx.cleanupFns) - 1; i >= 0; i-- {
err := ctx.cleanupFns[i](option)
if err != nil {
failed = true
if ctx.t != nil {
ctx.t.Errorf("A cleanup function failed with error: (%v)\n", err)
} else {
log.Errorf("A cleanup function failed with error: (%v)", err)
}
}
}
if ctx.t == nil && failed {
log.Fatal("A cleanup function failed")
}
}
func (ctx *TestCtx) AddCleanupFn(fn cleanupFn) {
ctx.cleanupFns = append(ctx.cleanupFns, fn)
}
func WaitForJobSucceed(c client.Client, namespace, name string, retryInterval, timeout time.Duration) error {
return waitForJobStatus(c, namespace, name, batchv1.JobComplete, retryInterval, timeout)
}
func WaitForJobFail(c client.Client, namespace, name string, retryInterval, timeout time.Duration) error {
return waitForJobStatus(c, namespace, name, batchv1.JobFailed, retryInterval, timeout)
}
func waitForJobStatus(c client.Client, namespace, name string, jobstatus batchv1.JobConditionType, retryInterval, timeout time.Duration) error {
err := wait.Poll(retryInterval, timeout, func() (done bool, err error) {
job := &batchv1.Job{}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
err = c.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, job)
if apierrors.IsNotFound(err) {
klog.Infof("Cannot find job %s", name)
return false, nil
}
if err != nil {
klog.Errorf("Get error %s when waiting for job up", err.Error())
return false, err
}
if len(job.Status.Conditions) > 0 && job.Status.Conditions[0].Type == jobstatus {
return true, nil
}
return false, nil
})
return err
}
func EnsureNamespace(c client.Client, namespace string) error {
ns := &corev1.Namespace{}
ns.Name = namespace
err := c.Create(context.TODO(), ns)
if err != nil {
if apierrors.IsAlreadyExists(err) {
klog.Infof("Namespace %s is existed", namespace)
return nil
}
}
return err
}
func DeleteNamespace(c client.Client, namespace string) error {
ns := &corev1.Namespace{}
ns.Name = namespace
return c.Delete(context.TODO(), ns)
}

73
pkg/test/yaml.go Normal file
View File

@@ -0,0 +1,73 @@
package test
import (
"bufio"
"bytes"
"io"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
)
const maxExecutiveEmpties = 100
// Scanner scans a yaml manifest file for manifest tokens delimited by "---".
// See bufio.Scanner for semantics.
type Scanner struct {
reader *k8syaml.YAMLReader
token []byte // Last token returned by split.
err error // Sticky error.
empties int // Count of successive empty tokens.
done bool // Scan has finished.
}
func NewYAMLScanner(b []byte) *Scanner {
r := bufio.NewReader(bytes.NewBuffer(b))
return &Scanner{reader: k8syaml.NewYAMLReader(r)}
}
func (s *Scanner) Err() error {
if s.err == io.EOF {
return nil
}
return s.err
}
func (s *Scanner) Scan() bool {
if s.done {
return false
}
var (
tok []byte
err error
)
for {
tok, err = s.reader.Read()
if err != nil {
if err == io.EOF {
s.done = true
}
s.err = err
return false
}
if len(bytes.TrimSpace(tok)) == 0 {
s.empties++
if s.empties > maxExecutiveEmpties {
panic("yaml.Scan: too many empty tokens without progressing")
}
continue
}
s.empties = 0
s.token = tok
return true
}
}
func (s *Scanner) Text() string {
return string(s.token)
}
func (s *Scanner) Bytes() []byte {
return s.token
}

View File

@@ -0,0 +1,40 @@
package e2e_test
import (
"flag"
"os"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/test"
)
var ctx *test.TestCtx
func TestE2e(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Networking E2e Suite")
}
var _ = BeforeSuite(func() {
klog.InitFlags(nil)
flag.Set("logtostderr", "false")
flag.Set("alsologtostderr", "false")
flag.Set("v", "4")
flag.Parse()
klog.SetOutput(GinkgoWriter)
ctx = test.NewTestCtx(nil, os.Getenv("TEST_NAMESPACE"))
Expect(ctx.Setup(os.Getenv("YAML_PATH"), "", v1alpha1.AddToScheme)).ShouldNot(HaveOccurred())
deployName := os.Getenv("DEPLOY_NAME")
Expect(test.WaitForController(ctx.Client, ctx.Namespace, deployName, 1, time.Second*5, time.Minute)).ShouldNot(HaveOccurred(), "Controlller failed to start")
klog.Infoln("Controller is up, begin to test ")
})
var _ = AfterSuite(func() {
ctx.Cleanup(nil)
})

157
test/e2e/e2e_test.go Normal file
View File

@@ -0,0 +1,157 @@
package e2e_test
import (
"context"
"time"
"k8s.io/klog"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes/scheme"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/test"
"sigs.k8s.io/controller-runtime/pkg/client"
)
var simpleDeployYaml = `apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: production
labels:
name: nginx
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
name: nginx
app: nginx
color : red
spec:
containers:
- image: nginx:alpine
name: nginx
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: "20m"
memory: "55M"
env:
- name: ENVVARNAME
value: ENVVARVALUE
ports:
- containerPort: 80
name: http
restartPolicy: Always`
var simpleNPYaml = `apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
name: allow-icmp-only
namespace: production
spec:
selector: color == 'red'
ingress:
- action: Allow
protocol: ICMP
source:
selector: color == 'blue'
namespaceSelector: all()`
var simpleJobYaml = `apiVersion: batch/v1
kind: Job
metadata:
name: test-connect
namespace: production
spec:
template:
metadata:
labels:
color : blue
spec:
containers:
- name: test-connect
image: alpine
command: ["ping", "1.1.1.1"]
restartPolicy: Never
backoffLimit: 1`
var testNs = "production"
var _ = Describe("E2e for network policy", func() {
BeforeEach(func() {
Expect(test.EnsureNamespace(ctx.Client, testNs)).ShouldNot(HaveOccurred())
})
AfterEach(func() {
Expect(test.DeleteNamespace(ctx.Client, testNs)).ShouldNot(HaveOccurred())
ns := &corev1.Namespace{}
ns.Name = testNs
Expect(test.WaitForDeletion(ctx.Client, ns, time.Second*5, time.Minute)).ShouldNot(HaveOccurred())
})
It("Should work well in simple namespaceNetworkPolicy", func() {
decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode([]byte(simpleDeployYaml), nil, nil)
Expect(err).ShouldNot(HaveOccurred(), "Failed to parse yaml")
deploy := obj.(*appsv1.Deployment)
Expect(ctx.Client.Create(context.TODO(), obj)).ShouldNot(HaveOccurred())
Expect(test.WaitForController(ctx.Client, deploy.Namespace, deploy.Name, *deploy.Spec.Replicas, time.Second*2, time.Minute)).ShouldNot(HaveOccurred())
defer func() {
Expect(ctx.Client.Delete(context.TODO(), deploy)).ShouldNot(HaveOccurred())
}()
obj, _, err = decode([]byte(simpleNPYaml), nil, nil)
Expect(err).ShouldNot(HaveOccurred(), "Failed to parse networkpolicy yaml")
np := obj.(*v1alpha1.NamespaceNetworkPolicy)
Expect(ctx.Client.Create(context.TODO(), np)).ShouldNot(HaveOccurred())
defer func() {
Expect(ctx.Client.Delete(context.TODO(), np)).ShouldNot(HaveOccurred())
Expect(test.WaitForDeletion(ctx.Client, np, time.Second*2, time.Minute)).ShouldNot(HaveOccurred())
}()
obj, _, err = decode([]byte(simpleJobYaml), nil, nil)
Expect(err).ShouldNot(HaveOccurred(), "Failed to parse job yaml")
//create a job to test
job := obj.(*batchv1.Job)
selector, _ := labels.Parse("app=nginx")
podlist := &corev1.PodList{}
Expect(ctx.Client.List(context.TODO(), &client.ListOptions{
Namespace: deploy.Namespace,
LabelSelector: selector,
}, podlist)).ShouldNot(HaveOccurred())
Expect(podlist.Items).To(HaveLen(int(*deploy.Spec.Replicas)))
podip := podlist.Items[0].Status.PodIP
job.Spec.Template.Spec.Containers[0].Command = []string{"ping", "-c", "4", podip}
job.Spec.Template.Labels["color"] = "yellow"
orginalJob := job.DeepCopy()
Expect(ctx.Client.Create(context.TODO(), job)).ShouldNot(HaveOccurred())
defer func() {
Expect(ctx.Client.Delete(context.TODO(), job)).ShouldNot(HaveOccurred())
}()
klog.Infoln("sleep 10s to wait for controller creating np")
time.Sleep(time.Second * 10)
Expect(test.WaitForJobFail(ctx.Client, job.Namespace, job.Name, time.Second*3, time.Minute)).ShouldNot(HaveOccurred(), "Failed to block connection")
//change job color
job = orginalJob.DeepCopy()
Expect(ctx.Client.Delete(context.TODO(), job)).ShouldNot(HaveOccurred())
Expect(test.WaitForDeletion(ctx.Client, job, time.Second*2, time.Minute)).ShouldNot(HaveOccurred())
job.Spec.Template.Labels["color"] = "blue"
Expect(ctx.Client.Create(context.TODO(), job)).ShouldNot(HaveOccurred())
Expect(test.WaitForJobSucceed(ctx.Client, job.Namespace, job.Name, time.Second*3, time.Minute)).ShouldNot(HaveOccurred(), "Connection failed")
})
})

13
test/network/Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM golang:1.12
RUN apt-get update && apt-get install -y apt-transport-https jq openssl libltdl7 && \
go get -u github.com/onsi/ginkgo/ginkgo && \
curl -s https://api.github.com/repos/kubernetes-sigs/kustomize/releases/latest |\
grep browser_download |\
grep linux |\
cut -d '"' -f 4 |\
xargs curl -O -L && \
mv kustomize_*_linux_amd64 kustomize && \
chmod u+x kustomize && \
mv kustomize /usr/bin/

43
test/network/Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,43 @@
pipeline {
agent {
docker {
image 'magicsong/nete2e:v0.0.1'
args '-v gomod:/go -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker'
}
}
environment {
KUBECONFIG = "/root/.kube/config"
}
stages {
stage('set kubeconfig and secret'){
steps{
sh 'mkdir -p ~/.kube'
sh 'mkdir ./kustomize/network/etcd'
withCredentials([kubeconfigContent(credentialsId: 'net-kubeconfig', variable: 'KUBECONFIG_CONTENT'),
file(credentialsId: 'etcd-ca', variable: 'etcd_ca'),
file(credentialsId: 'etcd-crt', variable: 'etcd_crt'),
file(credentialsId: 'etcd-key', variable: 'etcd_key')]) {
sh 'echo "$KUBECONFIG_CONTENT" > ~/.kube/config'
sh "cp \${etcd_ca} ./kustomize/network/etcd/ca"
sh "cp \${etcd_key} ./kustomize/network/etcd/key"
sh "cp \${etcd_crt} ./kustomize/network/etcd/crt"
}
}
}
stage('testing') {
steps {
sh """
make network-e2e
"""
}
}
stage('cleanup'){
steps{
sh """
rm -rf ./kustomize/network/etcd
"""
}
}
}
}

11
test/network/OWNERS Normal file
View File

@@ -0,0 +1,11 @@
approvers:
- magicsong
- zheng1
reviewers:
- magicsong
- zheng1
labels:
- area/testing
- area/networking

View File

@@ -0,0 +1,54 @@
apiVersion: network.kubesphere.io/v1alpha1
kind: NamespaceNetworkPolicy
metadata:
name: allow-icmp-only
namespace: production
spec:
selector: color == 'red'
ingress:
- action: Allow
protocol: ICMP
source:
selector: "all()"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: production
labels:
name: nginx
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
name: nginx
app: nginx
color : red
spec:
containers:
- image: nginx:alpine
name: nginx
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: "20m"
memory: "55M"
env:
- name: ENVVARNAME
value: ENVVARVALUE
ports:
- containerPort: 80
name: http
restartPolicy: Always

View File

@@ -0,0 +1,17 @@
apiVersion: batch/v1
kind: Job
metadata:
name: test-connect
namespace: production
spec:
template:
metadata:
labels:
color : blue
spec:
containers:
- name: test-connect
image: alpine
command: ["ping", "10.17.30.131"]
restartPolicy: Never
backoffLimit: 1

67
test/network/test.sh Executable file
View File

@@ -0,0 +1,67 @@
#!/bin/bash
set -e
workspace=`pwd`
tag=`git rev-parse --short HEAD`
IMG=magicsong/ks-network:$tag
DEST=/tmp/manager.yaml
TEST_NS=network-test-$tag
SKIP_BUILD=no
export TEST_NAMESPACE=$TEST_NS
export YAML_PATH=$DEST
export CRD_PATH=$workspace/kustomize/crds
export DEPLOY_NAME=network-manager
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-s|--skip-build)
SKIP_BUILD=yes
shift # past argument
;;
-n|--NAMESPACE)
TEST_NS=$2
shift # past argument
shift # past value
;;
-t|--tag)
tag="$2"
shift # past argument
shift # past value
;;
--default)
DEFAULT=YES
shift # past argument
;;
*) # unknown option
POSITIONAL+=("$1") # save it in an array for later
shift # past argument
;;
esac
done
if [ $SKIP_BUILD == "no" ]; then
echo "Building binary"
hack/gobuild.sh cmd/ks-network
docker build -f build/ks-network/Dockerfile -t $IMG bin/cmd
echo "Push images"
docker push $IMG
fi
kustomize_dir="./kustomize/network"
if [ "$(uname)" == "Darwin" ]; then
sed -i '' -e 's/namespace: .*/namespace: '"${TEST_NS}"'/' $kustomize_dir/kustomization.yaml
sed -i '' -e 's/namespace: .*/namespace: '"${TEST_NS}"'/' $kustomize_dir/patch_role_binding.yaml
sed -i '' -e 's@image: .*@image: '"${IMG}"'@' $kustomize_dir/patch_image_name.yaml
else
sed -i -e 's/namespace: .*/namespace: '"${TEST_NS}"'/' $kustomize_dir/patch_role_binding.yaml
sed -i -e 's/namespace: .*/namespace: '"${TEST_NS}"'/' $kustomize_dir/kustomization.yaml
sed -i -e 's@image: .*@image: '"${IMG}"'@' $kustomize_dir/patch_image_name.yaml
fi
kustomize build $kustomize_dir -o $DEST
ginkgo -v ./test/e2e/...

15
vendor/cloud.google.com/go/AUTHORS generated vendored Normal file
View File

@@ -0,0 +1,15 @@
# This is the official list of cloud authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as:
# Name or Organization <email address>
# The email address is not required for organizations.
Filippo Valsorda <hi@filippo.io>
Google Inc.
Ingo Oeser <nightlyone@googlemail.com>
Palm Stone Games, Inc.
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Tyler Treat <ttreat31@gmail.com>

40
vendor/cloud.google.com/go/CONTRIBUTORS generated vendored Normal file
View File

@@ -0,0 +1,40 @@
# People who have agreed to one of the CLAs and can contribute patches.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# https://developers.google.com/open-source/cla/individual
# https://developers.google.com/open-source/cla/corporate
#
# Names should be added to this file as:
# Name <email address>
# Keep the list alphabetically sorted.
Alexis Hunt <lexer@google.com>
Andreas Litt <andreas.litt@gmail.com>
Andrew Gerrand <adg@golang.org>
Brad Fitzpatrick <bradfitz@golang.org>
Burcu Dogan <jbd@google.com>
Dave Day <djd@golang.org>
David Sansome <me@davidsansome.com>
David Symonds <dsymonds@golang.org>
Filippo Valsorda <hi@filippo.io>
Glenn Lewis <gmlewis@google.com>
Ingo Oeser <nightlyone@googlemail.com>
James Hall <james.hall@shopify.com>
Johan Euphrosine <proppy@google.com>
Jonathan Amsterdam <jba@google.com>
Kunpei Sakai <namusyaka@gmail.com>
Luna Duclos <luna.duclos@palmstonegames.com>
Magnus Hiie <magnus.hiie@gmail.com>
Mario Castro <mariocaster@gmail.com>
Michael McGreevy <mcgreevy@golang.org>
Omar Jarjur <ojarjur@google.com>
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Sarah Adams <shadams@google.com>
Thanatat Tamtan <acoshift@gmail.com>
Toby Burress <kurin@google.com>
Tuo Shan <shantuo@google.com>
Tyler Treat <ttreat31@gmail.com>

202
vendor/cloud.google.com/go/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

501
vendor/cloud.google.com/go/compute/metadata/metadata.go generated vendored Normal file
View File

@@ -0,0 +1,501 @@
// Copyright 2014 Google LLC
//
// 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 metadata provides access to Google Compute Engine (GCE)
// metadata and API service accounts.
//
// This package is a wrapper around the GCE metadata service,
// as documented at https://developers.google.com/compute/docs/metadata.
package metadata // import "cloud.google.com/go/compute/metadata"
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"runtime"
"strings"
"sync"
"time"
)
const (
// metadataIP is the documented metadata server IP address.
metadataIP = "169.254.169.254"
// metadataHostEnv is the environment variable specifying the
// GCE metadata hostname. If empty, the default value of
// metadataIP ("169.254.169.254") is used instead.
// This is variable name is not defined by any spec, as far as
// I know; it was made up for the Go package.
metadataHostEnv = "GCE_METADATA_HOST"
userAgent = "gcloud-golang/0.1"
)
type cachedValue struct {
k string
trim bool
mu sync.Mutex
v string
}
var (
projID = &cachedValue{k: "project/project-id", trim: true}
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
instID = &cachedValue{k: "instance/id", trim: true}
)
var (
defaultClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
ResponseHeaderTimeout: 2 * time.Second,
},
}}
subscribeClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
},
}}
)
// NotDefinedError is returned when requested metadata is not defined.
//
// The underlying string is the suffix after "/computeMetadata/v1/".
//
// This error is not returned if the value is defined to be the empty
// string.
type NotDefinedError string
func (suffix NotDefinedError) Error() string {
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
}
func (c *cachedValue) get(cl *Client) (v string, err error) {
defer c.mu.Unlock()
c.mu.Lock()
if c.v != "" {
return c.v, nil
}
if c.trim {
v, err = cl.getTrimmed(c.k)
} else {
v, err = cl.Get(c.k)
}
if err == nil {
c.v = v
}
return
}
var (
onGCEOnce sync.Once
onGCE bool
)
// OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool {
onGCEOnce.Do(initOnGCE)
return onGCE
}
func initOnGCE() {
onGCE = testOnGCE()
}
func testOnGCE() bool {
// The user explicitly said they're on GCE, so trust them.
if os.Getenv(metadataHostEnv) != "" {
return true
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
resc := make(chan bool, 2)
// Try two strategies in parallel.
// See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194
go func() {
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
req.Header.Set("User-Agent", userAgent)
res, err := defaultClient.hc.Do(req.WithContext(ctx))
if err != nil {
resc <- false
return
}
defer res.Body.Close()
resc <- res.Header.Get("Metadata-Flavor") == "Google"
}()
go func() {
addrs, err := net.LookupHost("metadata.google.internal")
if err != nil || len(addrs) == 0 {
resc <- false
return
}
resc <- strsContains(addrs, metadataIP)
}()
tryHarder := systemInfoSuggestsGCE()
if tryHarder {
res := <-resc
if res {
// The first strategy succeeded, so let's use it.
return true
}
// Wait for either the DNS or metadata server probe to
// contradict the other one and say we are running on
// GCE. Give it a lot of time to do so, since the system
// info already suggests we're running on a GCE BIOS.
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
select {
case res = <-resc:
return res
case <-timer.C:
// Too slow. Who knows what this system is.
return false
}
}
// There's no hint from the system info that we're running on
// GCE, so use the first probe's result as truth, whether it's
// true or false. The goal here is to optimize for speed for
// users who are NOT running on GCE. We can't assume that
// either a DNS lookup or an HTTP request to a blackholed IP
// address is fast. Worst case this should return when the
// metaClient's Transport.ResponseHeaderTimeout or
// Transport.Dial.Timeout fires (in two seconds).
return <-resc
}
// systemInfoSuggestsGCE reports whether the local system (without
// doing network requests) suggests that we're running on GCE. If this
// returns true, testOnGCE tries a bit harder to reach its metadata
// server.
func systemInfoSuggestsGCE() bool {
if runtime.GOOS != "linux" {
// We don't have any non-Linux clues available, at least yet.
return false
}
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
name := strings.TrimSpace(string(slurp))
return name == "Google" || name == "Google Compute Engine"
}
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
// ResponseHeaderTimeout).
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
return subscribeClient.Subscribe(suffix, fn)
}
// Get calls Client.Get on the default client.
func Get(suffix string) (string, error) { return defaultClient.Get(suffix) }
// ProjectID returns the current instance's project ID string.
func ProjectID() (string, error) { return defaultClient.ProjectID() }
// NumericProjectID returns the current instance's numeric project ID.
func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() }
// InternalIP returns the instance's primary internal IP address.
func InternalIP() (string, error) { return defaultClient.InternalIP() }
// ExternalIP returns the instance's primary external (public) IP address.
func ExternalIP() (string, error) { return defaultClient.ExternalIP() }
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func Hostname() (string, error) { return defaultClient.Hostname() }
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() }
// InstanceID returns the current VM's numeric instance ID.
func InstanceID() (string, error) { return defaultClient.InstanceID() }
// InstanceName returns the current VM's instance ID string.
func InstanceName() (string, error) { return defaultClient.InstanceName() }
// Zone returns the current VM's zone, such as "us-central1-b".
func Zone() (string, error) { return defaultClient.Zone() }
// InstanceAttributes calls Client.InstanceAttributes on the default client.
func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() }
// ProjectAttributes calls Client.ProjectAttributes on the default client.
func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() }
// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client.
func InstanceAttributeValue(attr string) (string, error) {
return defaultClient.InstanceAttributeValue(attr)
}
// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client.
func ProjectAttributeValue(attr string) (string, error) {
return defaultClient.ProjectAttributeValue(attr)
}
// Scopes calls Client.Scopes on the default client.
func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) }
func strsContains(ss []string, s string) bool {
for _, v := range ss {
if v == s {
return true
}
}
return false
}
// A Client provides metadata.
type Client struct {
hc *http.Client
}
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
// will use the given http.Client instead of the default client.
func NewClient(c *http.Client) *Client {
return &Client{hc: c}
}
// getETag returns a value from the metadata service as well as the associated ETag.
// This func is otherwise equivalent to Get.
func (c *Client) getETag(suffix string) (value, etag string, err error) {
// Using a fixed IP makes it very difficult to spoof the metadata service in
// a container, which is an important use-case for local testing of cloud
// deployments. To enable spoofing of the metadata service, the environment
// variable GCE_METADATA_HOST is first inspected to decide where metadata
// requests shall go.
host := os.Getenv(metadataHostEnv)
if host == "" {
// Using 169.254.169.254 instead of "metadata" here because Go
// binaries built with the "netgo" tag and without cgo won't
// know the search suffix for "metadata" is
// ".google.internal", and this IP address is documented as
// being stable anyway.
host = metadataIP
}
url := "http://" + host + "/computeMetadata/v1/" + suffix
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Metadata-Flavor", "Google")
req.Header.Set("User-Agent", userAgent)
res, err := c.hc.Do(req)
if err != nil {
return "", "", err
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return "", "", NotDefinedError(suffix)
}
if res.StatusCode != 200 {
return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
}
all, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", err
}
return string(all), res.Header.Get("Etag"), nil
}
// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func (c *Client) Get(suffix string) (string, error) {
val, _, err := c.getETag(suffix)
return val, err
}
func (c *Client) getTrimmed(suffix string) (s string, err error) {
s, err = c.Get(suffix)
s = strings.TrimSpace(s)
return
}
func (c *Client) lines(suffix string) ([]string, error) {
j, err := c.Get(suffix)
if err != nil {
return nil, err
}
s := strings.Split(strings.TrimSpace(j), "\n")
for i := range s {
s[i] = strings.TrimSpace(s[i])
}
return s, nil
}
// ProjectID returns the current instance's project ID string.
func (c *Client) ProjectID() (string, error) { return projID.get(c) }
// NumericProjectID returns the current instance's numeric project ID.
func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) }
// InstanceID returns the current VM's numeric instance ID.
func (c *Client) InstanceID() (string, error) { return instID.get(c) }
// InternalIP returns the instance's primary internal IP address.
func (c *Client) InternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/ip")
}
// ExternalIP returns the instance's primary external (public) IP address.
func (c *Client) ExternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
}
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func (c *Client) Hostname() (string, error) {
return c.getTrimmed("instance/hostname")
}
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func (c *Client) InstanceTags() ([]string, error) {
var s []string
j, err := c.Get("instance/tags")
if err != nil {
return nil, err
}
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
return nil, err
}
return s, nil
}
// InstanceName returns the current VM's instance ID string.
func (c *Client) InstanceName() (string, error) {
host, err := c.Hostname()
if err != nil {
return "", err
}
return strings.Split(host, ".")[0], nil
}
// Zone returns the current VM's zone, such as "us-central1-b".
func (c *Client) Zone() (string, error) {
zone, err := c.getTrimmed("instance/zone")
// zone is of the form "projects/<projNum>/zones/<zoneName>".
if err != nil {
return "", err
}
return zone[strings.LastIndex(zone, "/")+1:], nil
}
// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") }
// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM. The value of
// an attribute can be obtained with ProjectAttributeValue.
func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") }
// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) InstanceAttributeValue(attr string) (string, error) {
return c.Get("instance/attributes/" + attr)
}
// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) ProjectAttributeValue(attr string) (string, error) {
return c.Get("project/attributes/" + attr)
}
// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func (c *Client) Scopes(serviceAccount string) ([]string, error) {
if serviceAccount == "" {
serviceAccount = "default"
}
return c.lines("instance/service-accounts/" + serviceAccount + "/scopes")
}
// Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters.
//
// Subscribe calls fn with the latest metadata value indicated by the provided
// suffix. If the metadata value is deleted, fn is called with the empty string
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
// is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false.
func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
const failedSubscribeSleep = time.Second * 5
// First check to see if the metadata value exists at all.
val, lastETag, err := c.getETag(suffix)
if err != nil {
return err
}
if err := fn(val, true); err != nil {
return err
}
ok := true
if strings.ContainsRune(suffix, '?') {
suffix += "&wait_for_change=true&last_etag="
} else {
suffix += "?wait_for_change=true&last_etag="
}
for {
val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag))
if err != nil {
if _, deleted := err.(NotDefinedError); !deleted {
time.Sleep(failedSubscribeSleep)
continue // Retry on other errors.
}
ok = false
}
lastETag = etag
if err := fn(val, ok); err != nil || !ok {
return err
}
}
}

View File

@@ -0,0 +1,18 @@
language: go
go:
- 1.11.x
go_import_path: contrib.go.opencensus.io/exporter/ocagent
before_script:
- GO_FILES=$(find . -iname '*.go' | grep -v /vendor/) # All the .go files, excluding vendor/ if any
- PKGS=$(go list ./... | grep -v /vendor/) # All the import paths, excluding vendor/ if any
script:
- go build ./... # Ensure dependency updates don't break build
- if [ -n "$(gofmt -s -l $GO_FILES)" ]; then echo "gofmt the following files:"; gofmt -s -l $GO_FILES; exit 1; fi
- go vet ./...
- GO111MODULE=on go test -v -race $PKGS # Run all the tests with the race detector enabled
- GO111MODULE=off go test -v -race $PKGS # Make sure tests still pass when not using Go modules.
- 'if [[ $TRAVIS_GO_VERSION = 1.8* ]]; then ! golint ./... | grep -vE "(_mock|_string|\.pb)\.go:"; fi'

View File

@@ -0,0 +1,24 @@
# How to contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution,
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult [GitHub Help] for more
information on using pull requests.
[GitHub Help]: https://help.github.com/articles/about-pull-requests/

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -0,0 +1,61 @@
# OpenCensus Agent Go Exporter
[![Build Status][travis-image]][travis-url] [![GoDoc][godoc-image]][godoc-url]
This repository contains the Go implementation of the OpenCensus Agent (OC-Agent) Exporter.
OC-Agent is a deamon process running in a VM that can retrieve spans/stats/metrics from
OpenCensus Library, export them to other backends and possibly push configurations back to
Library. See more details on [OC-Agent Readme][OCAgentReadme].
Note: This is an experimental repository and is likely to get backwards-incompatible changes.
Ultimately we may want to move the OC-Agent Go Exporter to [OpenCensus Go core library][OpenCensusGo].
## Installation
```bash
$ go get -u contrib.go.opencensus.io/exporter/ocagent
```
## Usage
```go
import (
"context"
"fmt"
"log"
"time"
"contrib.go.opencensus.io/exporter/ocagent"
"go.opencensus.io/trace"
)
func Example() {
exp, err := ocagent.NewExporter(ocagent.WithInsecure(), ocagent.WithServiceName("your-service-name"))
if err != nil {
log.Fatalf("Failed to create the agent exporter: %v", err)
}
defer exp.Stop()
// Now register it as a trace exporter.
trace.RegisterExporter(exp)
// Then use the OpenCensus tracing library, like we normally would.
ctx, span := trace.StartSpan(context.Background(), "AgentExporter-Example")
defer span.End()
for i := 0; i < 10; i++ {
_, iSpan := trace.StartSpan(ctx, fmt.Sprintf("Sample-%d", i))
<-time.After(6 * time.Millisecond)
iSpan.End()
}
}
```
[OCAgentReadme]: https://github.com/census-instrumentation/opencensus-proto/tree/master/opencensus/proto/agent#opencensus-agent-proto
[OpenCensusGo]: https://github.com/census-instrumentation/opencensus-go
[godoc-image]: https://godoc.org/contrib.go.opencensus.io/exporter/ocagent?status.svg
[godoc-url]: https://godoc.org/contrib.go.opencensus.io/exporter/ocagent
[travis-image]: https://travis-ci.org/census-ecosystem/opencensus-go-exporter-ocagent.svg?branch=master
[travis-url]: https://travis-ci.org/census-ecosystem/opencensus-go-exporter-ocagent

View File

@@ -0,0 +1,38 @@
// Copyright 2018, OpenCensus 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 ocagent
import (
"math/rand"
"time"
)
var randSrc = rand.New(rand.NewSource(time.Now().UnixNano()))
// retries function fn upto n times, if fn returns an error lest it returns nil early.
// It applies exponential backoff in units of (1<<n) + jitter microsends.
func nTriesWithExponentialBackoff(nTries int64, timeBaseUnit time.Duration, fn func() error) (err error) {
for i := int64(0); i < nTries; i++ {
err = fn()
if err == nil {
return nil
}
// Backoff for a time period with a pseudo-random jitter
jitter := time.Duration(randSrc.Float64()*100) * time.Microsecond
ts := jitter + ((1 << uint64(i)) * timeBaseUnit)
<-time.After(ts)
}
return err
}

View File

@@ -0,0 +1,97 @@
// Copyright 2018, OpenCensus 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 ocagent
import (
"math/rand"
"sync/atomic"
"time"
)
const (
sDisconnected int32 = 5 + iota
sConnected
)
func (ae *Exporter) setStateDisconnected() {
atomic.StoreInt32(&ae.connectionState, sDisconnected)
select {
case ae.disconnectedCh <- true:
default:
}
}
func (ae *Exporter) setStateConnected() {
atomic.StoreInt32(&ae.connectionState, sConnected)
}
func (ae *Exporter) connected() bool {
return atomic.LoadInt32(&ae.connectionState) == sConnected
}
const defaultConnReattemptPeriod = 10 * time.Second
func (ae *Exporter) indefiniteBackgroundConnection() error {
defer func() {
ae.backgroundConnectionDoneCh <- true
}()
connReattemptPeriod := ae.reconnectionPeriod
if connReattemptPeriod <= 0 {
connReattemptPeriod = defaultConnReattemptPeriod
}
// No strong seeding required, nano time can
// already help with pseudo uniqueness.
rng := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63n(1024)))
// maxJitter: 1 + (70% of the connectionReattemptPeriod)
maxJitter := int64(1 + 0.7*float64(connReattemptPeriod))
for {
// Otherwise these will be the normal scenarios to enable
// reconnections if we trip out.
// 1. If we've stopped, return entirely
// 2. Otherwise block until we are disconnected, and
// then retry connecting
select {
case <-ae.stopCh:
return errStopped
case <-ae.disconnectedCh:
// Normal scenario that we'll wait for
}
if err := ae.connect(); err == nil {
ae.setStateConnected()
} else {
ae.setStateDisconnected()
}
// Apply some jitter to avoid lockstep retrials of other
// agent-exporters. Lockstep retrials could result in an
// innocent DDOS, by clogging the machine's resources and network.
jitter := time.Duration(rng.Int63n(maxJitter))
<-time.After(connReattemptPeriod + jitter)
}
}
func (ae *Exporter) connect() error {
cc, err := ae.dialToAgent()
if err != nil {
return err
}
return ae.enableConnectionStreams(cc)
}

View File

@@ -0,0 +1,10 @@
module contrib.go.opencensus.io/exporter/ocagent
require (
github.com/census-instrumentation/opencensus-proto v0.2.0 // this is to match the version used in census-instrumentation/opencensus-service
github.com/golang/protobuf v1.3.1
github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect
go.opencensus.io v0.20.2
google.golang.org/api v0.3.1
google.golang.org/grpc v1.19.1
)

130
vendor/contrib.go.opencensus.io/exporter/ocagent/go.sum generated vendored Normal file
View File

@@ -0,0 +1,130 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
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=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
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 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -0,0 +1,46 @@
// Copyright 2018, OpenCensus 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 ocagent
import (
"os"
commonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1"
"go.opencensus.io"
)
// NodeWithStartTime creates a node using nodeName and derives:
// Hostname from the environment
// Pid from the current process
// StartTimestamp from the start time of this process
// Language and library information.
func NodeWithStartTime(nodeName string) *commonpb.Node {
return &commonpb.Node{
Identifier: &commonpb.ProcessIdentifier{
HostName: os.Getenv("HOSTNAME"),
Pid: uint32(os.Getpid()),
StartTimestamp: timeToTimestamp(startTime),
},
LibraryInfo: &commonpb.LibraryInfo{
Language: commonpb.LibraryInfo_GO_LANG,
ExporterVersion: Version,
CoreLibraryVersion: opencensus.Version(),
},
ServiceInfo: &commonpb.ServiceInfo{
Name: nodeName,
},
Attributes: make(map[string]string),
}
}

View File

@@ -0,0 +1,496 @@
// Copyright 2018, OpenCensus 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 ocagent
import (
"context"
"errors"
"fmt"
"sync"
"time"
"google.golang.org/api/support/bundler"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"go.opencensus.io/plugin/ocgrpc"
"go.opencensus.io/resource"
"go.opencensus.io/stats/view"
"go.opencensus.io/trace"
commonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1"
agentmetricspb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/metrics/v1"
agenttracepb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/trace/v1"
metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1"
resourcepb "github.com/census-instrumentation/opencensus-proto/gen-go/resource/v1"
tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1"
)
var startupMu sync.Mutex
var startTime time.Time
func init() {
startupMu.Lock()
startTime = time.Now()
startupMu.Unlock()
}
var _ trace.Exporter = (*Exporter)(nil)
var _ view.Exporter = (*Exporter)(nil)
type Exporter struct {
connectionState int32
// mu protects the non-atomic and non-channel variables
mu sync.RWMutex
// senderMu protects the concurrent unsafe traceExporter client
senderMu sync.RWMutex
started bool
stopped bool
agentAddress string
serviceName string
canDialInsecure bool
traceExporter agenttracepb.TraceService_ExportClient
metricsExporter agentmetricspb.MetricsService_ExportClient
nodeInfo *commonpb.Node
grpcClientConn *grpc.ClientConn
reconnectionPeriod time.Duration
resource *resourcepb.Resource
compressor string
headers map[string]string
startOnce sync.Once
stopCh chan bool
disconnectedCh chan bool
backgroundConnectionDoneCh chan bool
traceBundler *bundler.Bundler
// viewDataBundler is the bundler to enable conversion
// from OpenCensus-Go view.Data to metricspb.Metric.
// Please do not confuse it with metricsBundler!
viewDataBundler *bundler.Bundler
clientTransportCredentials credentials.TransportCredentials
}
func NewExporter(opts ...ExporterOption) (*Exporter, error) {
exp, err := NewUnstartedExporter(opts...)
if err != nil {
return nil, err
}
if err := exp.Start(); err != nil {
return nil, err
}
return exp, nil
}
const spanDataBufferSize = 300
func NewUnstartedExporter(opts ...ExporterOption) (*Exporter, error) {
e := new(Exporter)
for _, opt := range opts {
opt.withExporter(e)
}
traceBundler := bundler.NewBundler((*trace.SpanData)(nil), func(bundle interface{}) {
e.uploadTraces(bundle.([]*trace.SpanData))
})
traceBundler.DelayThreshold = 2 * time.Second
traceBundler.BundleCountThreshold = spanDataBufferSize
e.traceBundler = traceBundler
viewDataBundler := bundler.NewBundler((*view.Data)(nil), func(bundle interface{}) {
e.uploadViewData(bundle.([]*view.Data))
})
viewDataBundler.DelayThreshold = 2 * time.Second
viewDataBundler.BundleCountThreshold = 500 // TODO: (@odeke-em) make this configurable.
e.viewDataBundler = viewDataBundler
e.nodeInfo = NodeWithStartTime(e.serviceName)
e.resource = resourceProtoFromEnv()
return e, nil
}
const (
maxInitialConfigRetries = 10
maxInitialTracesRetries = 10
)
var (
errAlreadyStarted = errors.New("already started")
errNotStarted = errors.New("not started")
errStopped = errors.New("stopped")
errNoConnection = errors.New("no active connection")
)
// Start dials to the agent, establishing a connection to it. It also
// initiates the Config and Trace services by sending over the initial
// messages that consist of the node identifier. Start invokes a background
// connector that will reattempt connections to the agent periodically
// if the connection dies.
func (ae *Exporter) Start() error {
var err = errAlreadyStarted
ae.startOnce.Do(func() {
ae.mu.Lock()
defer ae.mu.Unlock()
ae.started = true
ae.disconnectedCh = make(chan bool, 1)
ae.stopCh = make(chan bool)
ae.backgroundConnectionDoneCh = make(chan bool)
ae.setStateDisconnected()
go ae.indefiniteBackgroundConnection()
err = nil
})
return err
}
func (ae *Exporter) prepareAgentAddress() string {
if ae.agentAddress != "" {
return ae.agentAddress
}
return fmt.Sprintf("%s:%d", DefaultAgentHost, DefaultAgentPort)
}
func (ae *Exporter) enableConnectionStreams(cc *grpc.ClientConn) error {
ae.mu.RLock()
started := ae.started
nodeInfo := ae.nodeInfo
ae.mu.RUnlock()
if !started {
return errNotStarted
}
ae.mu.Lock()
// If the previous clientConn was non-nil, close it
if ae.grpcClientConn != nil {
_ = ae.grpcClientConn.Close()
}
ae.grpcClientConn = cc
ae.mu.Unlock()
if err := ae.createTraceServiceConnection(ae.grpcClientConn, nodeInfo); err != nil {
return err
}
return ae.createMetricsServiceConnection(ae.grpcClientConn, nodeInfo)
}
func (ae *Exporter) createTraceServiceConnection(cc *grpc.ClientConn, node *commonpb.Node) error {
// Initiate the trace service by sending over node identifier info.
traceSvcClient := agenttracepb.NewTraceServiceClient(cc)
ctx := context.Background()
if len(ae.headers) > 0 {
ctx = metadata.NewOutgoingContext(ctx, metadata.New(ae.headers))
}
traceExporter, err := traceSvcClient.Export(ctx)
if err != nil {
return fmt.Errorf("Exporter.Start:: TraceServiceClient: %v", err)
}
firstTraceMessage := &agenttracepb.ExportTraceServiceRequest{
Node: node,
Resource: ae.resource,
}
if err := traceExporter.Send(firstTraceMessage); err != nil {
return fmt.Errorf("Exporter.Start:: Failed to initiate the Config service: %v", err)
}
ae.mu.Lock()
ae.traceExporter = traceExporter
ae.mu.Unlock()
// Initiate the config service by sending over node identifier info.
configStream, err := traceSvcClient.Config(context.Background())
if err != nil {
return fmt.Errorf("Exporter.Start:: ConfigStream: %v", err)
}
firstCfgMessage := &agenttracepb.CurrentLibraryConfig{Node: node}
if err := configStream.Send(firstCfgMessage); err != nil {
return fmt.Errorf("Exporter.Start:: Failed to initiate the Config service: %v", err)
}
// In the background, handle trace configurations that are beamed down
// by the agent, but also reply to it with the applied configuration.
go ae.handleConfigStreaming(configStream)
return nil
}
func (ae *Exporter) createMetricsServiceConnection(cc *grpc.ClientConn, node *commonpb.Node) error {
metricsSvcClient := agentmetricspb.NewMetricsServiceClient(cc)
metricsExporter, err := metricsSvcClient.Export(context.Background())
if err != nil {
return fmt.Errorf("MetricsExporter: failed to start the service client: %v", err)
}
// Initiate the metrics service by sending over the first message just containing the Node and Resource.
firstMetricsMessage := &agentmetricspb.ExportMetricsServiceRequest{
Node: node,
Resource: ae.resource,
}
if err := metricsExporter.Send(firstMetricsMessage); err != nil {
return fmt.Errorf("MetricsExporter:: failed to send the first message: %v", err)
}
ae.mu.Lock()
ae.metricsExporter = metricsExporter
ae.mu.Unlock()
// With that we are good to go and can start sending metrics
return nil
}
func (ae *Exporter) dialToAgent() (*grpc.ClientConn, error) {
addr := ae.prepareAgentAddress()
var dialOpts []grpc.DialOption
if ae.clientTransportCredentials != nil {
dialOpts = append(dialOpts, grpc.WithTransportCredentials(ae.clientTransportCredentials))
} else if ae.canDialInsecure {
dialOpts = append(dialOpts, grpc.WithInsecure())
}
if ae.compressor != "" {
dialOpts = append(dialOpts, grpc.WithDefaultCallOptions(grpc.UseCompressor(ae.compressor)))
}
dialOpts = append(dialOpts, grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
ctx := context.Background()
if len(ae.headers) > 0 {
ctx = metadata.NewOutgoingContext(ctx, metadata.New(ae.headers))
}
return grpc.DialContext(ctx, addr, dialOpts...)
}
func (ae *Exporter) handleConfigStreaming(configStream agenttracepb.TraceService_ConfigClient) error {
// Note: We haven't yet implemented configuration sending so we
// should NOT be changing connection states within this function for now.
for {
recv, err := configStream.Recv()
if err != nil {
// TODO: Check if this is a transient error or exponential backoff-able.
return err
}
cfg := recv.Config
if cfg == nil {
continue
}
// Otherwise now apply the trace configuration sent down from the agent
if psamp := cfg.GetProbabilitySampler(); psamp != nil {
trace.ApplyConfig(trace.Config{DefaultSampler: trace.ProbabilitySampler(psamp.SamplingProbability)})
} else if csamp := cfg.GetConstantSampler(); csamp != nil {
alwaysSample := csamp.Decision == tracepb.ConstantSampler_ALWAYS_ON
if alwaysSample {
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
} else {
trace.ApplyConfig(trace.Config{DefaultSampler: trace.NeverSample()})
}
} else { // TODO: Add the rate limiting sampler here
}
// Then finally send back to upstream the newly applied configuration
err = configStream.Send(&agenttracepb.CurrentLibraryConfig{Config: &tracepb.TraceConfig{Sampler: cfg.Sampler}})
if err != nil {
return err
}
}
}
// Stop shuts down all the connections and resources
// related to the exporter.
func (ae *Exporter) Stop() error {
ae.mu.RLock()
cc := ae.grpcClientConn
started := ae.started
stopped := ae.stopped
ae.mu.RUnlock()
if !started {
return errNotStarted
}
if stopped {
// TODO: tell the user that we've already stopped, so perhaps a sentinel error?
return nil
}
ae.Flush()
// Now close the underlying gRPC connection.
var err error
if cc != nil {
err = cc.Close()
}
// At this point we can change the state variables: started and stopped
ae.mu.Lock()
ae.started = false
ae.stopped = true
ae.mu.Unlock()
close(ae.stopCh)
// Ensure that the backgroundConnector returns
<-ae.backgroundConnectionDoneCh
return err
}
func (ae *Exporter) ExportSpan(sd *trace.SpanData) {
if sd == nil {
return
}
_ = ae.traceBundler.Add(sd, 1)
}
func (ae *Exporter) ExportTraceServiceRequest(batch *agenttracepb.ExportTraceServiceRequest) error {
if batch == nil || len(batch.Spans) == 0 {
return nil
}
select {
case <-ae.stopCh:
return errStopped
default:
if !ae.connected() {
return errNoConnection
}
ae.senderMu.Lock()
err := ae.traceExporter.Send(batch)
ae.senderMu.Unlock()
if err != nil {
ae.setStateDisconnected()
return err
}
return nil
}
}
func (ae *Exporter) ExportView(vd *view.Data) {
if vd == nil {
return
}
_ = ae.viewDataBundler.Add(vd, 1)
}
func ocSpanDataToPbSpans(sdl []*trace.SpanData) []*tracepb.Span {
if len(sdl) == 0 {
return nil
}
protoSpans := make([]*tracepb.Span, 0, len(sdl))
for _, sd := range sdl {
if sd != nil {
protoSpans = append(protoSpans, ocSpanToProtoSpan(sd))
}
}
return protoSpans
}
func (ae *Exporter) uploadTraces(sdl []*trace.SpanData) {
select {
case <-ae.stopCh:
return
default:
if !ae.connected() {
return
}
protoSpans := ocSpanDataToPbSpans(sdl)
if len(protoSpans) == 0 {
return
}
ae.senderMu.Lock()
err := ae.traceExporter.Send(&agenttracepb.ExportTraceServiceRequest{
Spans: protoSpans,
})
ae.senderMu.Unlock()
if err != nil {
ae.setStateDisconnected()
}
}
}
func ocViewDataToPbMetrics(vdl []*view.Data) []*metricspb.Metric {
if len(vdl) == 0 {
return nil
}
metrics := make([]*metricspb.Metric, 0, len(vdl))
for _, vd := range vdl {
if vd != nil {
vmetric, err := viewDataToMetric(vd)
// TODO: (@odeke-em) somehow report this error, if it is non-nil.
if err == nil && vmetric != nil {
metrics = append(metrics, vmetric)
}
}
}
return metrics
}
func (ae *Exporter) uploadViewData(vdl []*view.Data) {
select {
case <-ae.stopCh:
return
default:
if !ae.connected() {
return
}
protoMetrics := ocViewDataToPbMetrics(vdl)
if len(protoMetrics) == 0 {
return
}
err := ae.metricsExporter.Send(&agentmetricspb.ExportMetricsServiceRequest{
Metrics: protoMetrics,
// TODO:(@odeke-em)
// a) Figure out how to derive a Node from the environment
// b) Figure out how to derive a Resource from the environment
// or better letting users of the exporter configure it.
})
if err != nil {
ae.setStateDisconnected()
}
}
}
func (ae *Exporter) Flush() {
ae.traceBundler.Flush()
ae.viewDataBundler.Flush()
}
func resourceProtoFromEnv() *resourcepb.Resource {
rs, _ := resource.FromEnv(context.Background())
if rs == nil {
return nil
}
rprs := &resourcepb.Resource{
Type: rs.Type,
}
if rs.Labels != nil {
rprs.Labels = make(map[string]string)
for k, v := range rs.Labels {
rprs.Labels[k] = v
}
}
return rprs
}

View File

@@ -0,0 +1,128 @@
// Copyright 2018, OpenCensus 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 ocagent
import (
"time"
"google.golang.org/grpc/credentials"
)
const (
DefaultAgentPort uint16 = 55678
DefaultAgentHost string = "localhost"
)
type ExporterOption interface {
withExporter(e *Exporter)
}
type insecureGrpcConnection int
var _ ExporterOption = (*insecureGrpcConnection)(nil)
func (igc *insecureGrpcConnection) withExporter(e *Exporter) {
e.canDialInsecure = true
}
// WithInsecure disables client transport security for the exporter's gRPC connection
// just like grpc.WithInsecure() https://godoc.org/google.golang.org/grpc#WithInsecure
// does. Note, by default, client security is required unless WithInsecure is used.
func WithInsecure() ExporterOption { return new(insecureGrpcConnection) }
type addressSetter string
func (as addressSetter) withExporter(e *Exporter) {
e.agentAddress = string(as)
}
var _ ExporterOption = (*addressSetter)(nil)
// WithAddress allows one to set the address that the exporter will
// connect to the agent on. If unset, it will instead try to use
// connect to DefaultAgentHost:DefaultAgentPort
func WithAddress(addr string) ExporterOption {
return addressSetter(addr)
}
type serviceNameSetter string
func (sns serviceNameSetter) withExporter(e *Exporter) {
e.serviceName = string(sns)
}
var _ ExporterOption = (*serviceNameSetter)(nil)
// WithServiceName allows one to set/override the service name
// that the exporter will report to the agent.
func WithServiceName(serviceName string) ExporterOption {
return serviceNameSetter(serviceName)
}
type reconnectionPeriod time.Duration
func (rp reconnectionPeriod) withExporter(e *Exporter) {
e.reconnectionPeriod = time.Duration(rp)
}
func WithReconnectionPeriod(rp time.Duration) ExporterOption {
return reconnectionPeriod(rp)
}
type compressorSetter string
func (c compressorSetter) withExporter(e *Exporter) {
e.compressor = string(c)
}
// UseCompressor will set the compressor for the gRPC client to use when sending requests.
// It is the responsibility of the caller to ensure that the compressor set has been registered
// with google.golang.org/grpc/encoding. This can be done by encoding.RegisterCompressor. Some
// compressors auto-register on import, such as gzip, which can be registered by calling
// `import _ "google.golang.org/grpc/encoding/gzip"`
func UseCompressor(compressorName string) ExporterOption {
return compressorSetter(compressorName)
}
type headerSetter map[string]string
func (h headerSetter) withExporter(e *Exporter) {
e.headers = map[string]string(h)
}
// WithHeaders will send the provided headers when the gRPC stream connection
// is instantiated
func WithHeaders(headers map[string]string) ExporterOption {
return headerSetter(headers)
}
type clientCredentials struct {
credentials.TransportCredentials
}
var _ ExporterOption = (*clientCredentials)(nil)
// WithTLSCredentials allows the connection to use TLS credentials
// when talking to the server. It takes in grpc.TransportCredentials instead
// of say a Certificate file or a tls.Certificate, because the retrieving
// these credentials can be done in many ways e.g. plain file, in code tls.Config
// or by certificate rotation, so it is up to the caller to decide what to use.
func WithTLSCredentials(creds credentials.TransportCredentials) ExporterOption {
return &clientCredentials{TransportCredentials: creds}
}
func (cc *clientCredentials) withExporter(e *Exporter) {
e.clientTransportCredentials = cc.TransportCredentials
}

View File

@@ -0,0 +1,248 @@
// Copyright 2018, OpenCensus 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 ocagent
import (
"math"
"time"
"go.opencensus.io/trace"
"go.opencensus.io/trace/tracestate"
tracepb "github.com/census-instrumentation/opencensus-proto/gen-go/trace/v1"
"github.com/golang/protobuf/ptypes/timestamp"
)
const (
maxAnnotationEventsPerSpan = 32
maxMessageEventsPerSpan = 128
)
func ocSpanToProtoSpan(sd *trace.SpanData) *tracepb.Span {
if sd == nil {
return nil
}
var namePtr *tracepb.TruncatableString
if sd.Name != "" {
namePtr = &tracepb.TruncatableString{Value: sd.Name}
}
return &tracepb.Span{
TraceId: sd.TraceID[:],
SpanId: sd.SpanID[:],
ParentSpanId: sd.ParentSpanID[:],
Status: ocStatusToProtoStatus(sd.Status),
StartTime: timeToTimestamp(sd.StartTime),
EndTime: timeToTimestamp(sd.EndTime),
Links: ocLinksToProtoLinks(sd.Links),
Kind: ocSpanKindToProtoSpanKind(sd.SpanKind),
Name: namePtr,
Attributes: ocAttributesToProtoAttributes(sd.Attributes),
TimeEvents: ocTimeEventsToProtoTimeEvents(sd.Annotations, sd.MessageEvents),
Tracestate: ocTracestateToProtoTracestate(sd.Tracestate),
}
}
var blankStatus trace.Status
func ocStatusToProtoStatus(status trace.Status) *tracepb.Status {
if status == blankStatus {
return nil
}
return &tracepb.Status{
Code: status.Code,
Message: status.Message,
}
}
func ocLinksToProtoLinks(links []trace.Link) *tracepb.Span_Links {
if len(links) == 0 {
return nil
}
sl := make([]*tracepb.Span_Link, 0, len(links))
for _, ocLink := range links {
// This redefinition is necessary to prevent ocLink.*ID[:] copies
// being reused -- in short we need a new ocLink per iteration.
ocLink := ocLink
sl = append(sl, &tracepb.Span_Link{
TraceId: ocLink.TraceID[:],
SpanId: ocLink.SpanID[:],
Type: ocLinkTypeToProtoLinkType(ocLink.Type),
})
}
return &tracepb.Span_Links{
Link: sl,
}
}
func ocLinkTypeToProtoLinkType(oct trace.LinkType) tracepb.Span_Link_Type {
switch oct {
case trace.LinkTypeChild:
return tracepb.Span_Link_CHILD_LINKED_SPAN
case trace.LinkTypeParent:
return tracepb.Span_Link_PARENT_LINKED_SPAN
default:
return tracepb.Span_Link_TYPE_UNSPECIFIED
}
}
func ocAttributesToProtoAttributes(attrs map[string]interface{}) *tracepb.Span_Attributes {
if len(attrs) == 0 {
return nil
}
outMap := make(map[string]*tracepb.AttributeValue)
for k, v := range attrs {
switch v := v.(type) {
case bool:
outMap[k] = &tracepb.AttributeValue{Value: &tracepb.AttributeValue_BoolValue{BoolValue: v}}
case int:
outMap[k] = &tracepb.AttributeValue{Value: &tracepb.AttributeValue_IntValue{IntValue: int64(v)}}
case int64:
outMap[k] = &tracepb.AttributeValue{Value: &tracepb.AttributeValue_IntValue{IntValue: v}}
case string:
outMap[k] = &tracepb.AttributeValue{
Value: &tracepb.AttributeValue_StringValue{
StringValue: &tracepb.TruncatableString{Value: v},
},
}
}
}
return &tracepb.Span_Attributes{
AttributeMap: outMap,
}
}
// This code is mostly copied from
// https://github.com/census-ecosystem/opencensus-go-exporter-stackdriver/blob/master/trace_proto.go#L46
func ocTimeEventsToProtoTimeEvents(as []trace.Annotation, es []trace.MessageEvent) *tracepb.Span_TimeEvents {
if len(as) == 0 && len(es) == 0 {
return nil
}
timeEvents := &tracepb.Span_TimeEvents{}
var annotations, droppedAnnotationsCount int
var messageEvents, droppedMessageEventsCount int
// Transform annotations
for i, a := range as {
if annotations >= maxAnnotationEventsPerSpan {
droppedAnnotationsCount = len(as) - i
break
}
annotations++
timeEvents.TimeEvent = append(timeEvents.TimeEvent,
&tracepb.Span_TimeEvent{
Time: timeToTimestamp(a.Time),
Value: transformAnnotationToTimeEvent(&a),
},
)
}
// Transform message events
for i, e := range es {
if messageEvents >= maxMessageEventsPerSpan {
droppedMessageEventsCount = len(es) - i
break
}
messageEvents++
timeEvents.TimeEvent = append(timeEvents.TimeEvent,
&tracepb.Span_TimeEvent{
Time: timeToTimestamp(e.Time),
Value: transformMessageEventToTimeEvent(&e),
},
)
}
// Process dropped counter
timeEvents.DroppedAnnotationsCount = clip32(droppedAnnotationsCount)
timeEvents.DroppedMessageEventsCount = clip32(droppedMessageEventsCount)
return timeEvents
}
func transformAnnotationToTimeEvent(a *trace.Annotation) *tracepb.Span_TimeEvent_Annotation_ {
return &tracepb.Span_TimeEvent_Annotation_{
Annotation: &tracepb.Span_TimeEvent_Annotation{
Description: &tracepb.TruncatableString{Value: a.Message},
Attributes: ocAttributesToProtoAttributes(a.Attributes),
},
}
}
func transformMessageEventToTimeEvent(e *trace.MessageEvent) *tracepb.Span_TimeEvent_MessageEvent_ {
return &tracepb.Span_TimeEvent_MessageEvent_{
MessageEvent: &tracepb.Span_TimeEvent_MessageEvent{
Type: tracepb.Span_TimeEvent_MessageEvent_Type(e.EventType),
Id: uint64(e.MessageID),
UncompressedSize: uint64(e.UncompressedByteSize),
CompressedSize: uint64(e.CompressedByteSize),
},
}
}
// clip32 clips an int to the range of an int32.
func clip32(x int) int32 {
if x < math.MinInt32 {
return math.MinInt32
}
if x > math.MaxInt32 {
return math.MaxInt32
}
return int32(x)
}
func timeToTimestamp(t time.Time) *timestamp.Timestamp {
nanoTime := t.UnixNano()
return &timestamp.Timestamp{
Seconds: nanoTime / 1e9,
Nanos: int32(nanoTime % 1e9),
}
}
func ocSpanKindToProtoSpanKind(kind int) tracepb.Span_SpanKind {
switch kind {
case trace.SpanKindClient:
return tracepb.Span_CLIENT
case trace.SpanKindServer:
return tracepb.Span_SERVER
default:
return tracepb.Span_SPAN_KIND_UNSPECIFIED
}
}
func ocTracestateToProtoTracestate(ts *tracestate.Tracestate) *tracepb.Span_Tracestate {
if ts == nil {
return nil
}
return &tracepb.Span_Tracestate{
Entries: ocTracestateEntriesToProtoTracestateEntries(ts.Entries()),
}
}
func ocTracestateEntriesToProtoTracestateEntries(entries []tracestate.Entry) []*tracepb.Span_Tracestate_Entry {
protoEntries := make([]*tracepb.Span_Tracestate_Entry, 0, len(entries))
for _, entry := range entries {
protoEntries = append(protoEntries, &tracepb.Span_Tracestate_Entry{
Key: entry.Key,
Value: entry.Value,
})
}
return protoEntries
}

View File

@@ -0,0 +1,274 @@
// Copyright 2018, OpenCensus 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 ocagent
import (
"errors"
"time"
"go.opencensus.io/stats"
"go.opencensus.io/stats/view"
"go.opencensus.io/tag"
"github.com/golang/protobuf/ptypes/timestamp"
metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1"
)
var (
errNilMeasure = errors.New("expecting a non-nil stats.Measure")
errNilView = errors.New("expecting a non-nil view.View")
errNilViewData = errors.New("expecting a non-nil view.Data")
)
func viewDataToMetric(vd *view.Data) (*metricspb.Metric, error) {
if vd == nil {
return nil, errNilViewData
}
descriptor, err := viewToMetricDescriptor(vd.View)
if err != nil {
return nil, err
}
timeseries, err := viewDataToTimeseries(vd)
if err != nil {
return nil, err
}
metric := &metricspb.Metric{
MetricDescriptor: descriptor,
Timeseries: timeseries,
}
return metric, nil
}
func viewToMetricDescriptor(v *view.View) (*metricspb.MetricDescriptor, error) {
if v == nil {
return nil, errNilView
}
if v.Measure == nil {
return nil, errNilMeasure
}
desc := &metricspb.MetricDescriptor{
Name: stringOrCall(v.Name, v.Measure.Name),
Description: stringOrCall(v.Description, v.Measure.Description),
Unit: v.Measure.Unit(),
Type: aggregationToMetricDescriptorType(v),
LabelKeys: tagKeysToLabelKeys(v.TagKeys),
}
return desc, nil
}
func stringOrCall(first string, call func() string) string {
if first != "" {
return first
}
return call()
}
type measureType uint
const (
measureUnknown measureType = iota
measureInt64
measureFloat64
)
func measureTypeFromMeasure(m stats.Measure) measureType {
switch m.(type) {
default:
return measureUnknown
case *stats.Float64Measure:
return measureFloat64
case *stats.Int64Measure:
return measureInt64
}
}
func aggregationToMetricDescriptorType(v *view.View) metricspb.MetricDescriptor_Type {
if v == nil || v.Aggregation == nil {
return metricspb.MetricDescriptor_UNSPECIFIED
}
if v.Measure == nil {
return metricspb.MetricDescriptor_UNSPECIFIED
}
switch v.Aggregation.Type {
case view.AggTypeCount:
// Cumulative on int64
return metricspb.MetricDescriptor_CUMULATIVE_INT64
case view.AggTypeDistribution:
// Cumulative types
return metricspb.MetricDescriptor_CUMULATIVE_DISTRIBUTION
case view.AggTypeLastValue:
// Gauge types
switch measureTypeFromMeasure(v.Measure) {
case measureFloat64:
return metricspb.MetricDescriptor_GAUGE_DOUBLE
case measureInt64:
return metricspb.MetricDescriptor_GAUGE_INT64
}
case view.AggTypeSum:
// Cumulative types
switch measureTypeFromMeasure(v.Measure) {
case measureFloat64:
return metricspb.MetricDescriptor_CUMULATIVE_DOUBLE
case measureInt64:
return metricspb.MetricDescriptor_CUMULATIVE_INT64
}
}
// For all other cases, return unspecified.
return metricspb.MetricDescriptor_UNSPECIFIED
}
func tagKeysToLabelKeys(tagKeys []tag.Key) []*metricspb.LabelKey {
labelKeys := make([]*metricspb.LabelKey, 0, len(tagKeys))
for _, tagKey := range tagKeys {
labelKeys = append(labelKeys, &metricspb.LabelKey{
Key: tagKey.Name(),
})
}
return labelKeys
}
func viewDataToTimeseries(vd *view.Data) ([]*metricspb.TimeSeries, error) {
if vd == nil || len(vd.Rows) == 0 {
return nil, nil
}
// Given that view.Data only contains Start, End
// the timestamps for all the row data will be the exact same
// per aggregation. However, the values will differ.
// Each row has its own tags.
startTimestamp := timeToProtoTimestamp(vd.Start)
endTimestamp := timeToProtoTimestamp(vd.End)
mType := measureTypeFromMeasure(vd.View.Measure)
timeseries := make([]*metricspb.TimeSeries, 0, len(vd.Rows))
// It is imperative that the ordering of "LabelValues" matches those
// of the Label keys in the metric descriptor.
for _, row := range vd.Rows {
labelValues := labelValuesFromTags(row.Tags)
point := rowToPoint(vd.View, row, endTimestamp, mType)
timeseries = append(timeseries, &metricspb.TimeSeries{
StartTimestamp: startTimestamp,
LabelValues: labelValues,
Points: []*metricspb.Point{point},
})
}
if len(timeseries) == 0 {
return nil, nil
}
return timeseries, nil
}
func timeToProtoTimestamp(t time.Time) *timestamp.Timestamp {
unixNano := t.UnixNano()
return &timestamp.Timestamp{
Seconds: int64(unixNano / 1e9),
Nanos: int32(unixNano % 1e9),
}
}
func rowToPoint(v *view.View, row *view.Row, endTimestamp *timestamp.Timestamp, mType measureType) *metricspb.Point {
pt := &metricspb.Point{
Timestamp: endTimestamp,
}
switch data := row.Data.(type) {
case *view.CountData:
pt.Value = &metricspb.Point_Int64Value{Int64Value: data.Value}
case *view.DistributionData:
pt.Value = &metricspb.Point_DistributionValue{
DistributionValue: &metricspb.DistributionValue{
Count: data.Count,
Sum: float64(data.Count) * data.Mean, // because Mean := Sum/Count
// TODO: Add Exemplar
Buckets: bucketsToProtoBuckets(data.CountPerBucket),
BucketOptions: &metricspb.DistributionValue_BucketOptions{
Type: &metricspb.DistributionValue_BucketOptions_Explicit_{
Explicit: &metricspb.DistributionValue_BucketOptions_Explicit{
Bounds: v.Aggregation.Buckets,
},
},
},
SumOfSquaredDeviation: data.SumOfSquaredDev,
}}
case *view.LastValueData:
setPointValue(pt, data.Value, mType)
case *view.SumData:
setPointValue(pt, data.Value, mType)
}
return pt
}
// Not returning anything from this function because metricspb.Point.is_Value is an unexported
// interface hence we just have to set its value by pointer.
func setPointValue(pt *metricspb.Point, value float64, mType measureType) {
if mType == measureInt64 {
pt.Value = &metricspb.Point_Int64Value{Int64Value: int64(value)}
} else {
pt.Value = &metricspb.Point_DoubleValue{DoubleValue: value}
}
}
func bucketsToProtoBuckets(countPerBucket []int64) []*metricspb.DistributionValue_Bucket {
distBuckets := make([]*metricspb.DistributionValue_Bucket, len(countPerBucket))
for i := 0; i < len(countPerBucket); i++ {
count := countPerBucket[i]
distBuckets[i] = &metricspb.DistributionValue_Bucket{
Count: count,
}
}
return distBuckets
}
func labelValuesFromTags(tags []tag.Tag) []*metricspb.LabelValue {
if len(tags) == 0 {
return nil
}
labelValues := make([]*metricspb.LabelValue, 0, len(tags))
for _, tag_ := range tags {
labelValues = append(labelValues, &metricspb.LabelValue{
Value: tag_.Value,
// It is imperative that we set the "HasValue" attribute,
// in order to distinguish missing a label from the empty string.
// https://godoc.org/github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1#LabelValue.HasValue
//
// OpenCensus-Go uses non-pointers for tags as seen by this function's arguments,
// so the best case that we can use to distinguish missing labels/tags from the
// empty string is by checking if the Tag.Key.Name() != "" to indicate that we have
// a value.
HasValue: tag_.Key.Name() != "",
})
}
return labelValues
}

View File

@@ -0,0 +1,17 @@
// Copyright 2018, OpenCensus 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 ocagent
const Version = "0.0.1"

191
vendor/github.com/Azure/go-autorest/autorest/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2015 Microsoft Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2015 Microsoft Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,292 @@
# Azure Active Directory authentication for Go
This is a standalone package for authenticating with Azure Active
Directory from other Go libraries and applications, in particular the [Azure SDK
for Go](https://github.com/Azure/azure-sdk-for-go).
Note: Despite the package's name it is not related to other "ADAL" libraries
maintained in the [github.com/AzureAD](https://github.com/AzureAD) org. Issues
should be opened in [this repo's](https://github.com/Azure/go-autorest/issues)
or [the SDK's](https://github.com/Azure/azure-sdk-for-go/issues) issue
trackers.
## Install
```bash
go get -u github.com/Azure/go-autorest/autorest/adal
```
## Usage
An Active Directory application is required in order to use this library. An application can be registered in the [Azure Portal](https://portal.azure.com/) by following these [guidelines](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications) or using the [Azure CLI](https://github.com/Azure/azure-cli).
### Register an Azure AD Application with secret
1. Register a new application with a `secret` credential
```
az ad app create \
--display-name example-app \
--homepage https://example-app/home \
--identifier-uris https://example-app/app \
--password secret
```
2. Create a service principal using the `Application ID` from previous step
```
az ad sp create --id "Application ID"
```
* Replace `Application ID` with `appId` from step 1.
### Register an Azure AD Application with certificate
1. Create a private key
```
openssl genrsa -out "example-app.key" 2048
```
2. Create the certificate
```
openssl req -new -key "example-app.key" -subj "/CN=example-app" -out "example-app.csr"
openssl x509 -req -in "example-app.csr" -signkey "example-app.key" -out "example-app.crt" -days 10000
```
3. Create the PKCS12 version of the certificate containing also the private key
```
openssl pkcs12 -export -out "example-app.pfx" -inkey "example-app.key" -in "example-app.crt" -passout pass:
```
4. Register a new application with the certificate content form `example-app.crt`
```
certificateContents="$(tail -n+2 "example-app.crt" | head -n-1)"
az ad app create \
--display-name example-app \
--homepage https://example-app/home \
--identifier-uris https://example-app/app \
--key-usage Verify --end-date 2018-01-01 \
--key-value "${certificateContents}"
```
5. Create a service principal using the `Application ID` from previous step
```
az ad sp create --id "APPLICATION_ID"
```
* Replace `APPLICATION_ID` with `appId` from step 4.
### Grant the necessary permissions
Azure relies on a Role-Based Access Control (RBAC) model to manage the access to resources at a fine-grained
level. There is a set of [pre-defined roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles)
which can be assigned to a service principal of an Azure AD application depending of your needs.
```
az role assignment create --assigner "SERVICE_PRINCIPAL_ID" --role "ROLE_NAME"
```
* Replace the `SERVICE_PRINCIPAL_ID` with the `appId` from previous step.
* Replace the `ROLE_NAME` with a role name of your choice.
It is also possible to define custom role definitions.
```
az role definition create --role-definition role-definition.json
```
* Check [custom roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-custom-roles) for more details regarding the content of `role-definition.json` file.
### Acquire Access Token
The common configuration used by all flows:
```Go
const activeDirectoryEndpoint = "https://login.microsoftonline.com/"
tenantID := "TENANT_ID"
oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID)
applicationID := "APPLICATION_ID"
callback := func(token adal.Token) error {
// This is called after the token is acquired
}
// The resource for which the token is acquired
resource := "https://management.core.windows.net/"
```
* Replace the `TENANT_ID` with your tenant ID.
* Replace the `APPLICATION_ID` with the value from previous section.
#### Client Credentials
```Go
applicationSecret := "APPLICATION_SECRET"
spt, err := adal.NewServicePrincipalToken(
oauthConfig,
appliationID,
applicationSecret,
resource,
callbacks...)
if err != nil {
return nil, err
}
// Acquire a new access token
err = spt.Refresh()
if (err == nil) {
token := spt.Token
}
```
* Replace the `APPLICATION_SECRET` with the `password` value from previous section.
#### Client Certificate
```Go
certificatePath := "./example-app.pfx"
certData, err := ioutil.ReadFile(certificatePath)
if err != nil {
return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err)
}
// Get the certificate and private key from pfx file
certificate, rsaPrivateKey, err := decodePkcs12(certData, "")
if err != nil {
return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
}
spt, err := adal.NewServicePrincipalTokenFromCertificate(
oauthConfig,
applicationID,
certificate,
rsaPrivateKey,
resource,
callbacks...)
// Acquire a new access token
err = spt.Refresh()
if (err == nil) {
token := spt.Token
}
```
* Update the certificate path to point to the example-app.pfx file which was created in previous section.
#### Device Code
```Go
oauthClient := &http.Client{}
// Acquire the device code
deviceCode, err := adal.InitiateDeviceAuth(
oauthClient,
oauthConfig,
applicationID,
resource)
if err != nil {
return nil, fmt.Errorf("Failed to start device auth flow: %s", err)
}
// Display the authentication message
fmt.Println(*deviceCode.Message)
// Wait here until the user is authenticated
token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
if err != nil {
return nil, fmt.Errorf("Failed to finish device auth flow: %s", err)
}
spt, err := adal.NewServicePrincipalTokenFromManualToken(
oauthConfig,
applicationID,
resource,
*token,
callbacks...)
if (err == nil) {
token := spt.Token
}
```
#### Username password authenticate
```Go
spt, err := adal.NewServicePrincipalTokenFromUsernamePassword(
oauthConfig,
applicationID,
username,
password,
resource,
callbacks...)
if (err == nil) {
token := spt.Token
}
```
#### Authorization code authenticate
``` Go
spt, err := adal.NewServicePrincipalTokenFromAuthorizationCode(
oauthConfig,
applicationID,
clientSecret,
authorizationCode,
redirectURI,
resource,
callbacks...)
err = spt.Refresh()
if (err == nil) {
token := spt.Token
}
```
### Command Line Tool
A command line tool is available in `cmd/adal.go` that can acquire a token for a given resource. It supports all flows mentioned above.
```
adal -h
Usage of ./adal:
-applicationId string
application id
-certificatePath string
path to pk12/PFC application certificate
-mode string
authentication mode (device, secret, cert, refresh) (default "device")
-resource string
resource for which the token is requested
-secret string
application secret
-tenantId string
tenant id
-tokenCachePath string
location of oath token cache (default "/home/cgc/.adal/accessToken.json")
```
Example acquire a token for `https://management.core.windows.net/` using device code flow:
```
adal -mode device \
-applicationId "APPLICATION_ID" \
-tenantId "TENANT_ID" \
-resource https://management.core.windows.net/
```

View File

@@ -0,0 +1,151 @@
package adal
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"errors"
"fmt"
"net/url"
)
const (
activeDirectoryEndpointTemplate = "%s/oauth2/%s%s"
)
// OAuthConfig represents the endpoints needed
// in OAuth operations
type OAuthConfig struct {
AuthorityEndpoint url.URL `json:"authorityEndpoint"`
AuthorizeEndpoint url.URL `json:"authorizeEndpoint"`
TokenEndpoint url.URL `json:"tokenEndpoint"`
DeviceCodeEndpoint url.URL `json:"deviceCodeEndpoint"`
}
// IsZero returns true if the OAuthConfig object is zero-initialized.
func (oac OAuthConfig) IsZero() bool {
return oac == OAuthConfig{}
}
func validateStringParam(param, name string) error {
if len(param) == 0 {
return fmt.Errorf("parameter '" + name + "' cannot be empty")
}
return nil
}
// NewOAuthConfig returns an OAuthConfig with tenant specific urls
func NewOAuthConfig(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) {
apiVer := "1.0"
return NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, tenantID, &apiVer)
}
// NewOAuthConfigWithAPIVersion returns an OAuthConfig with tenant specific urls.
// If apiVersion is not nil the "api-version" query parameter will be appended to the endpoint URLs with the specified value.
func NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, tenantID string, apiVersion *string) (*OAuthConfig, error) {
if err := validateStringParam(activeDirectoryEndpoint, "activeDirectoryEndpoint"); err != nil {
return nil, err
}
api := ""
// it's legal for tenantID to be empty so don't validate it
if apiVersion != nil {
if err := validateStringParam(*apiVersion, "apiVersion"); err != nil {
return nil, err
}
api = fmt.Sprintf("?api-version=%s", *apiVersion)
}
u, err := url.Parse(activeDirectoryEndpoint)
if err != nil {
return nil, err
}
authorityURL, err := u.Parse(tenantID)
if err != nil {
return nil, err
}
authorizeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "authorize", api))
if err != nil {
return nil, err
}
tokenURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "token", api))
if err != nil {
return nil, err
}
deviceCodeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "devicecode", api))
if err != nil {
return nil, err
}
return &OAuthConfig{
AuthorityEndpoint: *authorityURL,
AuthorizeEndpoint: *authorizeURL,
TokenEndpoint: *tokenURL,
DeviceCodeEndpoint: *deviceCodeURL,
}, nil
}
// MultiTenantOAuthConfig provides endpoints for primary and aulixiary tenant IDs.
type MultiTenantOAuthConfig interface {
PrimaryTenant() *OAuthConfig
AuxiliaryTenants() []*OAuthConfig
}
// OAuthOptions contains optional OAuthConfig creation arguments.
type OAuthOptions struct {
APIVersion string
}
func (c OAuthOptions) apiVersion() string {
if c.APIVersion != "" {
return fmt.Sprintf("?api-version=%s", c.APIVersion)
}
return "1.0"
}
// NewMultiTenantOAuthConfig creates an object that support multitenant OAuth configuration.
// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/authenticate-multi-tenant for more information.
func NewMultiTenantOAuthConfig(activeDirectoryEndpoint, primaryTenantID string, auxiliaryTenantIDs []string, options OAuthOptions) (MultiTenantOAuthConfig, error) {
if len(auxiliaryTenantIDs) == 0 || len(auxiliaryTenantIDs) > 3 {
return nil, errors.New("must specify one to three auxiliary tenants")
}
mtCfg := multiTenantOAuthConfig{
cfgs: make([]*OAuthConfig, len(auxiliaryTenantIDs)+1),
}
apiVer := options.apiVersion()
pri, err := NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, primaryTenantID, &apiVer)
if err != nil {
return nil, fmt.Errorf("failed to create OAuthConfig for primary tenant: %v", err)
}
mtCfg.cfgs[0] = pri
for i := range auxiliaryTenantIDs {
aux, err := NewOAuthConfig(activeDirectoryEndpoint, auxiliaryTenantIDs[i])
if err != nil {
return nil, fmt.Errorf("failed to create OAuthConfig for tenant '%s': %v", auxiliaryTenantIDs[i], err)
}
mtCfg.cfgs[i+1] = aux
}
return mtCfg, nil
}
type multiTenantOAuthConfig struct {
// first config in the slice is the primary tenant
cfgs []*OAuthConfig
}
func (m multiTenantOAuthConfig) PrimaryTenant() *OAuthConfig {
return m.cfgs[0]
}
func (m multiTenantOAuthConfig) AuxiliaryTenants() []*OAuthConfig {
return m.cfgs[1:]
}

View File

@@ -0,0 +1,242 @@
package adal
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
This file is largely based on rjw57/oauth2device's code, with the follow differences:
* scope -> resource, and only allow a single one
* receive "Message" in the DeviceCode struct and show it to users as the prompt
* azure-xplat-cli has the following behavior that this emulates:
- does not send client_secret during the token exchange
- sends resource again in the token exchange request
*/
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
const (
logPrefix = "autorest/adal/devicetoken:"
)
var (
// ErrDeviceGeneric represents an unknown error from the token endpoint when using device flow
ErrDeviceGeneric = fmt.Errorf("%s Error while retrieving OAuth token: Unknown Error", logPrefix)
// ErrDeviceAccessDenied represents an access denied error from the token endpoint when using device flow
ErrDeviceAccessDenied = fmt.Errorf("%s Error while retrieving OAuth token: Access Denied", logPrefix)
// ErrDeviceAuthorizationPending represents the server waiting on the user to complete the device flow
ErrDeviceAuthorizationPending = fmt.Errorf("%s Error while retrieving OAuth token: Authorization Pending", logPrefix)
// ErrDeviceCodeExpired represents the server timing out and expiring the code during device flow
ErrDeviceCodeExpired = fmt.Errorf("%s Error while retrieving OAuth token: Code Expired", logPrefix)
// ErrDeviceSlowDown represents the service telling us we're polling too often during device flow
ErrDeviceSlowDown = fmt.Errorf("%s Error while retrieving OAuth token: Slow Down", logPrefix)
// ErrDeviceCodeEmpty represents an empty device code from the device endpoint while using device flow
ErrDeviceCodeEmpty = fmt.Errorf("%s Error while retrieving device code: Device Code Empty", logPrefix)
// ErrOAuthTokenEmpty represents an empty OAuth token from the token endpoint when using device flow
ErrOAuthTokenEmpty = fmt.Errorf("%s Error while retrieving OAuth token: Token Empty", logPrefix)
errCodeSendingFails = "Error occurred while sending request for Device Authorization Code"
errCodeHandlingFails = "Error occurred while handling response from the Device Endpoint"
errTokenSendingFails = "Error occurred while sending request with device code for a token"
errTokenHandlingFails = "Error occurred while handling response from the Token Endpoint (during device flow)"
errStatusNotOK = "Error HTTP status != 200"
)
// DeviceCode is the object returned by the device auth endpoint
// It contains information to instruct the user to complete the auth flow
type DeviceCode struct {
DeviceCode *string `json:"device_code,omitempty"`
UserCode *string `json:"user_code,omitempty"`
VerificationURL *string `json:"verification_url,omitempty"`
ExpiresIn *int64 `json:"expires_in,string,omitempty"`
Interval *int64 `json:"interval,string,omitempty"`
Message *string `json:"message"` // Azure specific
Resource string // store the following, stored when initiating, used when exchanging
OAuthConfig OAuthConfig
ClientID string
}
// TokenError is the object returned by the token exchange endpoint
// when something is amiss
type TokenError struct {
Error *string `json:"error,omitempty"`
ErrorCodes []int `json:"error_codes,omitempty"`
ErrorDescription *string `json:"error_description,omitempty"`
Timestamp *string `json:"timestamp,omitempty"`
TraceID *string `json:"trace_id,omitempty"`
}
// DeviceToken is the object return by the token exchange endpoint
// It can either look like a Token or an ErrorToken, so put both here
// and check for presence of "Error" to know if we are in error state
type deviceToken struct {
Token
TokenError
}
// InitiateDeviceAuth initiates a device auth flow. It returns a DeviceCode
// that can be used with CheckForUserCompletion or WaitForUserCompletion.
func InitiateDeviceAuth(sender Sender, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) {
v := url.Values{
"client_id": []string{clientID},
"resource": []string{resource},
}
s := v.Encode()
body := ioutil.NopCloser(strings.NewReader(s))
req, err := http.NewRequest(http.MethodPost, oauthConfig.DeviceCodeEndpoint.String(), body)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error())
}
req.ContentLength = int64(len(s))
req.Header.Set(contentType, mimeTypeFormPost)
resp, err := sender.Do(req)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error())
}
defer resp.Body.Close()
rb, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error())
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, errStatusNotOK)
}
if len(strings.Trim(string(rb), " ")) == 0 {
return nil, ErrDeviceCodeEmpty
}
var code DeviceCode
err = json.Unmarshal(rb, &code)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error())
}
code.ClientID = clientID
code.Resource = resource
code.OAuthConfig = oauthConfig
return &code, nil
}
// CheckForUserCompletion takes a DeviceCode and checks with the Azure AD OAuth endpoint
// to see if the device flow has: been completed, timed out, or otherwise failed
func CheckForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
v := url.Values{
"client_id": []string{code.ClientID},
"code": []string{*code.DeviceCode},
"grant_type": []string{OAuthGrantTypeDeviceCode},
"resource": []string{code.Resource},
}
s := v.Encode()
body := ioutil.NopCloser(strings.NewReader(s))
req, err := http.NewRequest(http.MethodPost, code.OAuthConfig.TokenEndpoint.String(), body)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error())
}
req.ContentLength = int64(len(s))
req.Header.Set(contentType, mimeTypeFormPost)
resp, err := sender.Do(req)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error())
}
defer resp.Body.Close()
rb, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error())
}
if resp.StatusCode != http.StatusOK && len(strings.Trim(string(rb), " ")) == 0 {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, errStatusNotOK)
}
if len(strings.Trim(string(rb), " ")) == 0 {
return nil, ErrOAuthTokenEmpty
}
var token deviceToken
err = json.Unmarshal(rb, &token)
if err != nil {
return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error())
}
if token.Error == nil {
return &token.Token, nil
}
switch *token.Error {
case "authorization_pending":
return nil, ErrDeviceAuthorizationPending
case "slow_down":
return nil, ErrDeviceSlowDown
case "access_denied":
return nil, ErrDeviceAccessDenied
case "code_expired":
return nil, ErrDeviceCodeExpired
default:
return nil, ErrDeviceGeneric
}
}
// WaitForUserCompletion calls CheckForUserCompletion repeatedly until a token is granted or an error state occurs.
// This prevents the user from looping and checking against 'ErrDeviceAuthorizationPending'.
func WaitForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) {
intervalDuration := time.Duration(*code.Interval) * time.Second
waitDuration := intervalDuration
for {
token, err := CheckForUserCompletion(sender, code)
if err == nil {
return token, nil
}
switch err {
case ErrDeviceSlowDown:
waitDuration += waitDuration
case ErrDeviceAuthorizationPending:
// noop
default: // everything else is "fatal" to us
return nil, err
}
if waitDuration > (intervalDuration * 3) {
return nil, fmt.Errorf("%s Error waiting for user to complete device flow. Server told us to slow_down too much", logPrefix)
}
time.Sleep(waitDuration)
}
}

View File

@@ -0,0 +1,11 @@
module github.com/Azure/go-autorest/autorest/adal
go 1.12
require (
github.com/Azure/go-autorest/autorest/date v0.1.0
github.com/Azure/go-autorest/autorest/mocks v0.1.0
github.com/Azure/go-autorest/tracing v0.1.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
)

View File

@@ -0,0 +1,139 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
contrib.go.opencensus.io/exporter/ocagent v0.4.12 h1:jGFvw3l57ViIVEPKKEUXPcLYIXJmQxLUh6ey1eJhwyc=
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/tracing v0.1.0 h1:TRBxC5Pj/fIuh4Qob0ZpkggbfT8RC0SubHbpV3p4/Vc=
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
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=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
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 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -0,0 +1,73 @@
package adal
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
// LoadToken restores a Token object from a file located at 'path'.
func LoadToken(path string) (*Token, error) {
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err)
}
defer file.Close()
var token Token
dec := json.NewDecoder(file)
if err = dec.Decode(&token); err != nil {
return nil, fmt.Errorf("failed to decode contents of file (%s) into Token representation: %v", path, err)
}
return &token, nil
}
// SaveToken persists an oauth token at the given location on disk.
// It moves the new file into place so it can safely be used to replace an existing file
// that maybe accessed by multiple processes.
func SaveToken(path string, mode os.FileMode, token Token) error {
dir := filepath.Dir(path)
err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create directory (%s) to store token in: %v", dir, err)
}
newFile, err := ioutil.TempFile(dir, "token")
if err != nil {
return fmt.Errorf("failed to create the temp file to write the token: %v", err)
}
tempPath := newFile.Name()
if err := json.NewEncoder(newFile).Encode(token); err != nil {
return fmt.Errorf("failed to encode token to file (%s) while saving token: %v", tempPath, err)
}
if err := newFile.Close(); err != nil {
return fmt.Errorf("failed to close temp file %s: %v", tempPath, err)
}
// Atomic replace to avoid multi-writer file corruptions
if err := os.Rename(tempPath, path); err != nil {
return fmt.Errorf("failed to move temporary token to desired output location. src=%s dst=%s: %v", tempPath, path, err)
}
if err := os.Chmod(path, mode); err != nil {
return fmt.Errorf("failed to chmod the token file %s: %v", path, err)
}
return nil
}

View File

@@ -0,0 +1,60 @@
package adal
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"net/http"
)
const (
contentType = "Content-Type"
mimeTypeFormPost = "application/x-www-form-urlencoded"
)
// Sender is the interface that wraps the Do method to send HTTP requests.
//
// The standard http.Client conforms to this interface.
type Sender interface {
Do(*http.Request) (*http.Response, error)
}
// SenderFunc is a method that implements the Sender interface.
type SenderFunc func(*http.Request) (*http.Response, error)
// Do implements the Sender interface on SenderFunc.
func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) {
return sf(r)
}
// SendDecorator takes and possibly decorates, by wrapping, a Sender. Decorators may affect the
// http.Request and pass it along or, first, pass the http.Request along then react to the
// http.Response result.
type SendDecorator func(Sender) Sender
// CreateSender creates, decorates, and returns, as a Sender, the default http.Client.
func CreateSender(decorators ...SendDecorator) Sender {
return DecorateSender(&http.Client{}, decorators...)
}
// DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to
// the Sender. Decorators are applied in the order received, but their affect upon the request
// depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a
// post-decorator (pass the http.Request along and react to the results in http.Response).
func DecorateSender(s Sender, decorators ...SendDecorator) Sender {
for _, decorate := range decorators {
s = decorate(s)
}
return s
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,45 @@
package adal
import (
"fmt"
"runtime"
)
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const number = "v1.0.0"
var (
ua = fmt.Sprintf("Go/%s (%s-%s) go-autorest/adal/%s",
runtime.Version(),
runtime.GOARCH,
runtime.GOOS,
number,
)
)
// UserAgent returns a string containing the Go version, system architecture and OS, and the adal version.
func UserAgent() string {
return ua
}
// AddToUserAgent adds an extension to the current user agent
func AddToUserAgent(extension string) error {
if extension != "" {
ua = fmt.Sprintf("%s %s", ua, extension)
return nil
}
return fmt.Errorf("Extension was empty, User Agent remained as '%s'", ua)
}

View File

@@ -0,0 +1,336 @@
package autorest
// Copyright 2017 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import (
"encoding/base64"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/tracing"
)
const (
bearerChallengeHeader = "Www-Authenticate"
bearer = "Bearer"
tenantID = "tenantID"
apiKeyAuthorizerHeader = "Ocp-Apim-Subscription-Key"
bingAPISdkHeader = "X-BingApis-SDK-Client"
golangBingAPISdkHeaderValue = "Go-SDK"
authorization = "Authorization"
basic = "Basic"
)
// Authorizer is the interface that provides a PrepareDecorator used to supply request
// authorization. Most often, the Authorizer decorator runs last so it has access to the full
// state of the formed HTTP request.
type Authorizer interface {
WithAuthorization() PrepareDecorator
}
// NullAuthorizer implements a default, "do nothing" Authorizer.
type NullAuthorizer struct{}
// WithAuthorization returns a PrepareDecorator that does nothing.
func (na NullAuthorizer) WithAuthorization() PrepareDecorator {
return WithNothing()
}
// APIKeyAuthorizer implements API Key authorization.
type APIKeyAuthorizer struct {
headers map[string]interface{}
queryParameters map[string]interface{}
}
// NewAPIKeyAuthorizerWithHeaders creates an ApiKeyAuthorizer with headers.
func NewAPIKeyAuthorizerWithHeaders(headers map[string]interface{}) *APIKeyAuthorizer {
return NewAPIKeyAuthorizer(headers, nil)
}
// NewAPIKeyAuthorizerWithQueryParameters creates an ApiKeyAuthorizer with query parameters.
func NewAPIKeyAuthorizerWithQueryParameters(queryParameters map[string]interface{}) *APIKeyAuthorizer {
return NewAPIKeyAuthorizer(nil, queryParameters)
}
// NewAPIKeyAuthorizer creates an ApiKeyAuthorizer with headers.
func NewAPIKeyAuthorizer(headers map[string]interface{}, queryParameters map[string]interface{}) *APIKeyAuthorizer {
return &APIKeyAuthorizer{headers: headers, queryParameters: queryParameters}
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP headers and Query Parameters.
func (aka *APIKeyAuthorizer) WithAuthorization() PrepareDecorator {
return func(p Preparer) Preparer {
return DecoratePreparer(p, WithHeaders(aka.headers), WithQueryParameters(aka.queryParameters))
}
}
// CognitiveServicesAuthorizer implements authorization for Cognitive Services.
type CognitiveServicesAuthorizer struct {
subscriptionKey string
}
// NewCognitiveServicesAuthorizer is
func NewCognitiveServicesAuthorizer(subscriptionKey string) *CognitiveServicesAuthorizer {
return &CognitiveServicesAuthorizer{subscriptionKey: subscriptionKey}
}
// WithAuthorization is
func (csa *CognitiveServicesAuthorizer) WithAuthorization() PrepareDecorator {
headers := make(map[string]interface{})
headers[apiKeyAuthorizerHeader] = csa.subscriptionKey
headers[bingAPISdkHeader] = golangBingAPISdkHeaderValue
return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
}
// BearerAuthorizer implements the bearer authorization
type BearerAuthorizer struct {
tokenProvider adal.OAuthTokenProvider
}
// NewBearerAuthorizer crates a BearerAuthorizer using the given token provider
func NewBearerAuthorizer(tp adal.OAuthTokenProvider) *BearerAuthorizer {
return &BearerAuthorizer{tokenProvider: tp}
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
// value is "Bearer " followed by the token.
//
// By default, the token will be automatically refreshed through the Refresher interface.
func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator {
return func(p Preparer) Preparer {
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
r, err := p.Prepare(r)
if err == nil {
// the ordering is important here, prefer RefresherWithContext if available
if refresher, ok := ba.tokenProvider.(adal.RefresherWithContext); ok {
err = refresher.EnsureFreshWithContext(r.Context())
} else if refresher, ok := ba.tokenProvider.(adal.Refresher); ok {
err = refresher.EnsureFresh()
}
if err != nil {
var resp *http.Response
if tokError, ok := err.(adal.TokenRefreshError); ok {
resp = tokError.Response()
}
return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", resp,
"Failed to refresh the Token for request to %s", r.URL)
}
return Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", ba.tokenProvider.OAuthToken())))
}
return r, err
})
}
}
// BearerAuthorizerCallbackFunc is the authentication callback signature.
type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error)
// BearerAuthorizerCallback implements bearer authorization via a callback.
type BearerAuthorizerCallback struct {
sender Sender
callback BearerAuthorizerCallbackFunc
}
// NewBearerAuthorizerCallback creates a bearer authorization callback. The callback
// is invoked when the HTTP request is submitted.
func NewBearerAuthorizerCallback(sender Sender, callback BearerAuthorizerCallbackFunc) *BearerAuthorizerCallback {
if sender == nil {
sender = &http.Client{Transport: tracing.Transport}
}
return &BearerAuthorizerCallback{sender: sender, callback: callback}
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose value
// is "Bearer " followed by the token. The BearerAuthorizer is obtained via a user-supplied callback.
//
// By default, the token will be automatically refreshed through the Refresher interface.
func (bacb *BearerAuthorizerCallback) WithAuthorization() PrepareDecorator {
return func(p Preparer) Preparer {
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
r, err := p.Prepare(r)
if err == nil {
// make a copy of the request and remove the body as it's not
// required and avoids us having to create a copy of it.
rCopy := *r
removeRequestBody(&rCopy)
resp, err := bacb.sender.Do(&rCopy)
if err == nil && resp.StatusCode == 401 {
defer resp.Body.Close()
if hasBearerChallenge(resp) {
bc, err := newBearerChallenge(resp)
if err != nil {
return r, err
}
if bacb.callback != nil {
ba, err := bacb.callback(bc.values[tenantID], bc.values["resource"])
if err != nil {
return r, err
}
return Prepare(r, ba.WithAuthorization())
}
}
}
}
return r, err
})
}
}
// returns true if the HTTP response contains a bearer challenge
func hasBearerChallenge(resp *http.Response) bool {
authHeader := resp.Header.Get(bearerChallengeHeader)
if len(authHeader) == 0 || strings.Index(authHeader, bearer) < 0 {
return false
}
return true
}
type bearerChallenge struct {
values map[string]string
}
func newBearerChallenge(resp *http.Response) (bc bearerChallenge, err error) {
challenge := strings.TrimSpace(resp.Header.Get(bearerChallengeHeader))
trimmedChallenge := challenge[len(bearer)+1:]
// challenge is a set of key=value pairs that are comma delimited
pairs := strings.Split(trimmedChallenge, ",")
if len(pairs) < 1 {
err = fmt.Errorf("challenge '%s' contains no pairs", challenge)
return bc, err
}
bc.values = make(map[string]string)
for i := range pairs {
trimmedPair := strings.TrimSpace(pairs[i])
pair := strings.Split(trimmedPair, "=")
if len(pair) == 2 {
// remove the enclosing quotes
key := strings.Trim(pair[0], "\"")
value := strings.Trim(pair[1], "\"")
switch key {
case "authorization", "authorization_uri":
// strip the tenant ID from the authorization URL
asURL, err := url.Parse(value)
if err != nil {
return bc, err
}
bc.values[tenantID] = asURL.Path[1:]
default:
bc.values[key] = value
}
}
}
return bc, err
}
// EventGridKeyAuthorizer implements authorization for event grid using key authentication.
type EventGridKeyAuthorizer struct {
topicKey string
}
// NewEventGridKeyAuthorizer creates a new EventGridKeyAuthorizer
// with the specified topic key.
func NewEventGridKeyAuthorizer(topicKey string) EventGridKeyAuthorizer {
return EventGridKeyAuthorizer{topicKey: topicKey}
}
// WithAuthorization returns a PrepareDecorator that adds the aeg-sas-key authentication header.
func (egta EventGridKeyAuthorizer) WithAuthorization() PrepareDecorator {
headers := map[string]interface{}{
"aeg-sas-key": egta.topicKey,
}
return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
}
// BasicAuthorizer implements basic HTTP authorization by adding the Authorization HTTP header
// with the value "Basic <TOKEN>" where <TOKEN> is a base64-encoded username:password tuple.
type BasicAuthorizer struct {
userName string
password string
}
// NewBasicAuthorizer creates a new BasicAuthorizer with the specified username and password.
func NewBasicAuthorizer(userName, password string) *BasicAuthorizer {
return &BasicAuthorizer{
userName: userName,
password: password,
}
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose
// value is "Basic " followed by the base64-encoded username:password tuple.
func (ba *BasicAuthorizer) WithAuthorization() PrepareDecorator {
headers := make(map[string]interface{})
headers[authorization] = basic + " " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", ba.userName, ba.password)))
return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization()
}
// MultiTenantServicePrincipalTokenAuthorizer provides authentication across tenants.
type MultiTenantServicePrincipalTokenAuthorizer interface {
WithAuthorization() PrepareDecorator
}
// NewMultiTenantServicePrincipalTokenAuthorizer crates a BearerAuthorizer using the given token provider
func NewMultiTenantServicePrincipalTokenAuthorizer(tp adal.MultitenantOAuthTokenProvider) MultiTenantServicePrincipalTokenAuthorizer {
return &multiTenantSPTAuthorizer{tp: tp}
}
type multiTenantSPTAuthorizer struct {
tp adal.MultitenantOAuthTokenProvider
}
// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header using the
// primary token along with the auxiliary authorization header using the auxiliary tokens.
//
// By default, the token will be automatically refreshed through the Refresher interface.
func (mt multiTenantSPTAuthorizer) WithAuthorization() PrepareDecorator {
return func(p Preparer) Preparer {
return PreparerFunc(func(r *http.Request) (*http.Request, error) {
r, err := p.Prepare(r)
if err != nil {
return r, err
}
if refresher, ok := mt.tp.(adal.RefresherWithContext); ok {
err = refresher.EnsureFreshWithContext(r.Context())
if err != nil {
var resp *http.Response
if tokError, ok := err.(adal.TokenRefreshError); ok {
resp = tokError.Response()
}
return r, NewErrorWithError(err, "azure.multiTenantSPTAuthorizer", "WithAuthorization", resp,
"Failed to refresh one or more Tokens for request to %s", r.URL)
}
}
r, err = Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", mt.tp.PrimaryOAuthToken())))
if err != nil {
return r, err
}
auxTokens := mt.tp.AuxiliaryOAuthTokens()
for i := range auxTokens {
auxTokens[i] = fmt.Sprintf("Bearer %s", auxTokens[i])
}
return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, "; ")))
})
}
}

Some files were not shown because too many files have changed in this diff Show More