diff --git a/build.Dockerfile b/build.Dockerfile index 439409c53..5b25c5f8f 100644 --- a/build.Dockerfile +++ b/build.Dockerfile @@ -21,6 +21,10 @@ WORKDIR /go/src/github.com/containous/traefik COPY glide.yaml glide.yaml COPY glide.lock glide.lock -RUN glide install +RUN glide install -v + +COPY integration/glide.yaml integration/glide.yaml +COPY integration/glide.lock integration/glide.lock +RUN cd integration && glide install COPY . /go/src/github.com/containous/traefik diff --git a/glide.lock b/glide.lock index e5daa50c8..13aa2c14f 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: aae2fd761966717bcc30d8c03590cc28df8af49b36cc1d87689512cdfb44475e -updated: 2016-11-16T21:37:51.673291661+01:00 +hash: 3ff2f92356bc5d286ce4d72835d5ace63c43cc591e561fd2d557e8dc8abd1672 +updated: 2016-11-17T17:17:58.806741449Z imports: - name: github.com/abbot/go-http-auth version: cb4372376e1e00e9f6ab9ec142e029302c9e7140 @@ -9,6 +9,8 @@ imports: - eureka - name: github.com/ArthurHlt/gominlog version: 068c01ce147ad68fca25ef3fa29ae5395ae273ab +- name: github.com/blang/semver + version: 60ec3488bfea7cca02b021d106d9911120d25fe9 - name: github.com/boltdb/bolt version: f4c032d907f61f08dba2d719c58f108a1abb8e81 - name: github.com/BurntSushi/toml @@ -19,10 +21,12 @@ imports: - fun - name: github.com/cenk/backoff version: 8edc80b07f38c27352fb186d971c628a6c32552b +- name: github.com/cloudfoundry-incubator/candiedyaml + version: 99c3df83b51532e3615f851d8c2dbb638f5313bf - name: github.com/codahale/hdrhistogram - version: f8ad88b59a584afeee9d334eff879b104439117b + version: 9208b142303c12d8899bae836fd524ac9338b4fd - name: github.com/codegangsta/cli - version: 1efa31f08b9333f1bd4882d61f9d668a70cd902e + version: bf4a526f48af7badd25d2cb02d587e1b01be3b50 - name: github.com/codegangsta/negroni version: 3f7ce7b928e14ff890b067e5bbbc80af73690a9c - name: github.com/containous/flaeg @@ -32,11 +36,28 @@ imports: - name: github.com/containous/staert version: 92329254783dc01174f03302d51d7cf2c9ff84cf - name: github.com/coreos/etcd - version: 1c9e0a0e33051fed6c05c141e6fcbfe5c7f2a899 + version: c400d05d0aa73e21e431c16145e558d624098018 subpackages: + - Godeps/_workspace/src/github.com/ugorji/go/codec + - Godeps/_workspace/src/golang.org/x/net/context - client - pkg/pathutil - pkg/types +- name: github.com/coreos/go-oidc + version: 16c5ecc505f1efa0fe4685826fd9962c4d137e87 + subpackages: + - http + - jose + - key + - oauth2 + - oidc +- name: github.com/coreos/pkg + version: 2c77715c4df99b5420ffcae14ead08f52104065d + subpackages: + - capnslog + - health + - httputil + - timeutil - name: github.com/davecgh/go-spew version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 subpackages: @@ -133,23 +154,6 @@ imports: version: f2145db703495b2e525c59662db69a7344b00bb8 - name: github.com/docker/leadership version: 0a913e2d71a12fd14a028452435cb71ac8d82cb6 -- name: github.com/docker/libcompose - version: d1876c1d68527a49c0aac22a0b161acc7296b740 - subpackages: - - config - - docker - - docker/builder - - docker/client - - docker/network - - labels - - logger - - lookup - - project - - project/events - - project/options - - utils - - version - - yaml - name: github.com/docker/libkv version: 3fce6a0f26e07da3eac45796a8e255547a47a750 subpackages: @@ -162,22 +166,42 @@ imports: version: fd1de70867126402be23c306e1ce32828455d85b - name: github.com/elazarl/go-bindata-assetfs version: 9a6736ed45b44bf3835afeebb3034b57ed329f3e +- name: github.com/emicklei/go-restful + version: 892402ba11a2e2fd5e1295dd633481f27365f14d + subpackages: + - log + - swagger - name: github.com/gambol99/go-marathon version: a558128c87724cd7430060ef5aedf39f83937f55 -- name: github.com/go-check/check - version: 4f90aeace3a26ad7021961c297b22c42160c7b25 +- name: github.com/ghodss/yaml + version: aa0c862057666179de291b67d9f093d12b5a8473 +- name: github.com/go-openapi/jsonpointer + version: 8d96a2dc61536b690bd36b2e9df0b3c0b62825b2 +- name: github.com/go-openapi/jsonreference + version: 36d33bfe519efae5632669801b180bf1a245da3b +- name: github.com/go-openapi/spec + version: d1c18b339aece4b16ead6d253b85b6ad7180ea54 +- name: github.com/go-openapi/swag + version: 3b6d86cd965820f968760d5d419cb4add096bdd7 - name: github.com/gogo/protobuf - version: 99cb9b23110011cc45571c901ecae6f6f5e65cd3 + version: 909568be09de550ed094403c2bf8a261b5bb730a subpackages: - proto + - sortkeys - name: github.com/golang/glog version: fca8c8854093a154ff1eb580aae10276ad6b1b5f +- name: github.com/golang/protobuf + version: 5677a0e3d5e89854c9974e1256839ee23f8233ca + subpackages: + - proto - name: github.com/google/go-querystring version: 9235644dd9e52eeae6fa48efd539fdc351a0af53 subpackages: - query +- name: github.com/google/gofuzz + version: fd52762d25a41827db7ef64c43756fd4b9f7e382 - name: github.com/gorilla/context - version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42 + version: 14f550f51af52180c2eefed15e5fd18d63c0a64a - name: github.com/hashicorp/consul version: d8e2fb7dd594163e25a89bc52c1a4613f5c5bfb8 subpackages: @@ -185,21 +209,25 @@ imports: - name: github.com/hashicorp/go-cleanhttp version: ad28ea4487f05916463e2423a55166280e8254b5 - name: github.com/hashicorp/serf - version: b03bf85930b2349eb04b97c8fac437495296e3e7 + version: 598c54895cc5a7b1a24a398d635e8c0ea0959870 subpackages: - coordinate - name: github.com/jarcoal/httpmock version: 145b10d659265440f062c31ea15326166bae56ee -- name: github.com/libkermit/compose - version: cadc5a3b83a15790174bd7fbc75ea2529785e772 - subpackages: - - check -- name: github.com/libkermit/docker - version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7 +- name: github.com/jonboulle/clockwork + version: 72f9bd7c4e0c2a40055ab3d0f09654f730cce982 +- name: github.com/juju/ratelimit + version: 77ed1c8a01217656d2080ad51981f6e99adaa177 - name: github.com/mailgun/manners version: a585afd9d65c0e05f6c003f921e71ebc05074f4f - name: github.com/mailgun/timetools version: fd192d755b00c968d312d23f521eb0cdc6f66bd0 +- name: github.com/mailru/easyjson + version: 159cdb893c982e3d1bc6450322fedd514f9c9de3 + subpackages: + - buffer + - jlexer + - jwriter - name: github.com/mattn/go-shellwords version: 525bedee691b5a8df547cb5cf9f86b7fb1883e24 - name: github.com/mesos/mesos-go @@ -230,22 +258,22 @@ imports: version: 5d001d020961ae1c184f9f8152fdc73810481677 - name: github.com/mitchellh/mapstructure version: ca63d7c062ee3c9f34db231e352b60012b4fd0c1 -- name: github.com/moul/http2curl - version: b1479103caacaa39319f75e7f57fc545287fca0d - name: github.com/NYTimes/gziphandler version: f6438dbf4a82c56684964b03956aa727b0d7816b - name: github.com/ogier/pflag version: 45c278ab3607870051a2ea9040bb85fcb8557481 - name: github.com/opencontainers/runc - version: 02f8fa7863dd3f82909a73e2061897828460d52f + version: ba1568de399395774ad84c2ace65937814c542ed subpackages: - libcontainer/user - name: github.com/parnurzeal/gorequest version: e30af16d4e485943aab0b0885ad6bdbb8c0d3dc7 -- name: github.com/pmezard/go-difflib - version: d8ed2627bdf02c080bf22230dbb337003b7aba2d - subpackages: - - difflib +- name: github.com/pborman/uuid + version: 3d4f2ba23642d3cfd06bd4b54cf03d99d95c0f1b +- name: github.com/PuerkitoBio/purell + version: 0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4 +- name: github.com/PuerkitoBio/urlesc + version: 5bd2802263f21d8788851d5305584c82a5c75d7e - name: github.com/ryanuber/go-glob version: 572520ed46dbddaed19ea3d9541bdd0494163693 - name: github.com/samuel/go-zookeeper @@ -256,12 +284,14 @@ imports: version: 879c5887cd475cd7864858769793b2ceb0d44feb - name: github.com/Sirupsen/logrus version: 3ec0642a7fb6488f65b06f9040adc67e3990296a +- name: github.com/spf13/pflag + version: 5644820622454e71517561946e3d94b9f9db6842 - name: github.com/streamrail/concurrent-map version: 8bf1e9bacbf65b10c81d0f4314cf2b1ebef728b5 - name: github.com/stretchr/objx version: cbeaeb16a013161a98496fad62933b1d21786672 - name: github.com/stretchr/testify - version: 976c720a22c8eb4eb6a0b4348ad85ad12491a506 + version: b8dc1cecf15bdaf1988d9e87aa7cd98d899a06d6 subpackages: - assert - mock @@ -270,15 +300,13 @@ imports: - name: github.com/tv42/zbase32 version: 03389da7e0bf9844767f82690f4d68fc097a1306 - name: github.com/ugorji/go - version: b94837a2404ab90efe9289e77a70694c355739cb + version: ea9cd21fa0bc41ee4bdd50ac7ed8cbc7ea2ed960 subpackages: - codec - name: github.com/unrolled/render version: 526faf80cd4b305bb8134abea8d20d5ced74faa6 - name: github.com/vdemeester/docker-events version: be74d4929ec1ad118df54349fda4b0cba60f849b -- name: github.com/vdemeester/shakers - version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 - name: github.com/vulcand/oxy version: 4298f24d572dc554eb984f2ffdf6bdd54d4bd613 repo: https://github.com/containous/oxy.git @@ -296,7 +324,7 @@ imports: - name: github.com/vulcand/route version: cb89d787ddbb1c5849a7ac9f79004c1fd12a4a32 - name: github.com/vulcand/vulcand - version: bed092e10989250b48bdb6aa3b0557b207f05c80 + version: 42492a3a85e294bdbdd1bcabb8c12769a81ea284 subpackages: - conntracker - plugin @@ -307,26 +335,62 @@ imports: subpackages: - acme - name: golang.org/x/crypto - version: d81fdb778bf2c40a91b24519d60cdc5767318829 + version: 4ed45ec682102c643324fae5dff8dab085b6c300 subpackages: - bcrypt - blowfish - ocsp - name: golang.org/x/net - version: b400c2eff1badec7022a8c8f5bea058b6315eed7 + version: d4c55e66d8c3a2f3382d264b08e3e3454a66355a subpackages: - context + - http2 + - http2/hpack + - idna + - lex/httplex - proxy - publicsuffix +- name: golang.org/x/oauth2 + version: 3046bc76d6dfd7d3707f6640f85e42d9c4050f50 + subpackages: + - google + - internal + - jws + - jwt - name: golang.org/x/sys - version: 62bee037599929a6e9146f29d10dd5208c43507d + version: eb2c74142fd19a79b3f237334c7384d5167b1b46 subpackages: - unix - windows +- name: golang.org/x/text + version: a263ba8db058568bb9beba166777d9c9dbe75d68 + subpackages: + - transform + - unicode/norm + - width +- name: google.golang.org/appengine + version: 12d5545dc1cfa6047a286d5e853841b6471f4c19 + subpackages: + - internal + - internal/app_identity + - internal/base + - internal/datastore + - internal/log + - internal/modules + - internal/remote_api + - internal/urlfetch + - urlfetch +- name: google.golang.org/cloud + version: f20d6dcccb44ed49de45ae3703312cb46e627db1 + subpackages: + - compute/metadata + - internal - name: gopkg.in/fsnotify.v1 version: 944cff21b3baf3ced9a880365682152ba577d348 +- name: gopkg.in/inf.v0 + version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/mgo.v2 - version: 22287bab4379e1fbf6002fb4eb769888f3fb224c + version: 29cc868a5ca65f401ff318143f9408d02f4799cc subpackages: - bson - name: gopkg.in/square/go-jose.v1 @@ -334,27 +398,156 @@ imports: subpackages: - cipher - json +- name: k8s.io/client-go + version: 843f7c4f28b1f647f664f883697107d5c02c5acc + subpackages: + - 1.5/discovery + - 1.5/kubernetes + - 1.5/kubernetes/typed/apps/v1alpha1 + - 1.5/kubernetes/typed/authentication/v1beta1 + - 1.5/kubernetes/typed/authorization/v1beta1 + - 1.5/kubernetes/typed/autoscaling/v1 + - 1.5/kubernetes/typed/batch/v1 + - 1.5/kubernetes/typed/certificates/v1alpha1 + - 1.5/kubernetes/typed/core/v1 + - 1.5/kubernetes/typed/extensions/v1beta1 + - 1.5/kubernetes/typed/policy/v1alpha1 + - 1.5/kubernetes/typed/rbac/v1alpha1 + - 1.5/kubernetes/typed/storage/v1beta1 + - 1.5/pkg/api + - 1.5/pkg/api/errors + - 1.5/pkg/api/install + - 1.5/pkg/api/meta + - 1.5/pkg/api/meta/metatypes + - 1.5/pkg/api/resource + - 1.5/pkg/api/unversioned + - 1.5/pkg/api/v1 + - 1.5/pkg/api/validation/path + - 1.5/pkg/apimachinery + - 1.5/pkg/apimachinery/announced + - 1.5/pkg/apimachinery/registered + - 1.5/pkg/apis/apps + - 1.5/pkg/apis/apps/install + - 1.5/pkg/apis/apps/v1alpha1 + - 1.5/pkg/apis/authentication + - 1.5/pkg/apis/authentication/install + - 1.5/pkg/apis/authentication/v1beta1 + - 1.5/pkg/apis/authorization + - 1.5/pkg/apis/authorization/install + - 1.5/pkg/apis/authorization/v1beta1 + - 1.5/pkg/apis/autoscaling + - 1.5/pkg/apis/autoscaling/install + - 1.5/pkg/apis/autoscaling/v1 + - 1.5/pkg/apis/batch + - 1.5/pkg/apis/batch/install + - 1.5/pkg/apis/batch/v1 + - 1.5/pkg/apis/batch/v2alpha1 + - 1.5/pkg/apis/certificates + - 1.5/pkg/apis/certificates/install + - 1.5/pkg/apis/certificates/v1alpha1 + - 1.5/pkg/apis/extensions + - 1.5/pkg/apis/extensions/install + - 1.5/pkg/apis/extensions/v1beta1 + - 1.5/pkg/apis/policy + - 1.5/pkg/apis/policy/install + - 1.5/pkg/apis/policy/v1alpha1 + - 1.5/pkg/apis/rbac + - 1.5/pkg/apis/rbac/install + - 1.5/pkg/apis/rbac/v1alpha1 + - 1.5/pkg/apis/storage + - 1.5/pkg/apis/storage/install + - 1.5/pkg/apis/storage/v1beta1 + - 1.5/pkg/auth/user + - 1.5/pkg/conversion + - 1.5/pkg/conversion/queryparams + - 1.5/pkg/fields + - 1.5/pkg/genericapiserver/openapi/common + - 1.5/pkg/labels + - 1.5/pkg/runtime + - 1.5/pkg/runtime/serializer + - 1.5/pkg/runtime/serializer/json + - 1.5/pkg/runtime/serializer/protobuf + - 1.5/pkg/runtime/serializer/recognizer + - 1.5/pkg/runtime/serializer/streaming + - 1.5/pkg/runtime/serializer/versioning + - 1.5/pkg/selection + - 1.5/pkg/third_party/forked/golang/reflect + - 1.5/pkg/types + - 1.5/pkg/util + - 1.5/pkg/util/cert + - 1.5/pkg/util/clock + - 1.5/pkg/util/errors + - 1.5/pkg/util/flowcontrol + - 1.5/pkg/util/framer + - 1.5/pkg/util/integer + - 1.5/pkg/util/intstr + - 1.5/pkg/util/json + - 1.5/pkg/util/labels + - 1.5/pkg/util/net + - 1.5/pkg/util/parsers + - 1.5/pkg/util/rand + - 1.5/pkg/util/runtime + - 1.5/pkg/util/sets + - 1.5/pkg/util/uuid + - 1.5/pkg/util/validation + - 1.5/pkg/util/validation/field + - 1.5/pkg/util/wait + - 1.5/pkg/util/yaml + - 1.5/pkg/version + - 1.5/pkg/watch + - 1.5/pkg/watch/versioned + - 1.5/plugin/pkg/client/auth + - 1.5/plugin/pkg/client/auth/gcp + - 1.5/plugin/pkg/client/auth/oidc + - 1.5/rest + - 1.5/tools/cache + - 1.5/tools/clientcmd/api + - 1.5/tools/metrics + - 1.5/transport testImports: - name: github.com/Azure/go-ansiterm version: fa152c58bc15761d0200cb75fe958b89a9d4888e subpackages: - winterm -- name: github.com/cloudfoundry-incubator/candiedyaml - version: 99c3df83b51532e3615f851d8c2dbb638f5313bf +- name: github.com/docker/libcompose + version: d1876c1d68527a49c0aac22a0b161acc7296b740 + subpackages: + - config + - docker + - docker/builder + - docker/client + - docker/network + - labels + - logger + - lookup + - project + - project/events + - project/options + - utils + - version + - yaml - name: github.com/flynn/go-shlex version: 3f9db97f856818214da2e1057f8ad84803971cff +- name: github.com/go-check/check + version: 11d3bc7aa68e238947792f30573146a3231fc0f1 - name: github.com/gorilla/mux - version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e + version: e444e69cbd2e2e3e0749a2f3c717cec491552bbf +- name: github.com/libkermit/compose + version: cadc5a3b83a15790174bd7fbc75ea2529785e772 + subpackages: + - check +- name: github.com/libkermit/docker + version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7 - name: github.com/libkermit/docker-check version: cbe0ef03b3d23070eac4d00ba8828f2cc7f7e5a3 -- name: github.com/spf13/pflag - version: 5644820622454e71517561946e3d94b9f9db6842 - name: github.com/vbatts/tar-split version: bd4c5d64c3e9297f410025a3b1bd0c58f659e721 subpackages: - archive/tar - tar/asm - tar/storage +- name: github.com/vdemeester/shakers + version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 - name: github.com/xeipuuv/gojsonpointer version: e0fe6f68307607d540ed8eac07a342c33fa1b54a - name: github.com/xeipuuv/gojsonreference diff --git a/glide.yaml b/glide.yaml index 33ae718fc..3673d1fff 100644 --- a/glide.yaml +++ b/glide.yaml @@ -58,26 +58,23 @@ import: - package: github.com/vdemeester/docker-events version: be74d4929ec1ad118df54349fda4b0cba60f849b - package: github.com/vulcand/vulcand + version: 42492a3a85e294bdbdd1bcabb8c12769a81ea284 subpackages: - plugin/rewrite - package: github.com/xenolf/lego version: b2fad6198110326662e9e356a97199078a4a775c subpackages: - acme +- package: golang.org/x/net + version: release-branch.go1.7 + subpackages: + - context - package: gopkg.in/fsnotify.v1 -- package: github.com/libkermit/compose - version: cadc5a3b83a15790174bd7fbc75ea2529785e772 -- package: github.com/libkermit/docker - version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7 - package: github.com/docker/docker version: 534753663161334baba06f13b8efa4cad22b5bc5 subpackages: - namesgenerator -- package: github.com/go-check/check -- package: github.com/docker/libcompose - version: d1876c1d68527a49c0aac22a0b161acc7296b740 - package: github.com/mattn/go-shellwords -- package: github.com/vdemeester/shakers - package: github.com/ryanuber/go-glob - package: github.com/mesos/mesos-go subpackages: @@ -99,6 +96,10 @@ import: - package: github.com/docker/leadership - package: github.com/satori/go.uuid version: ^1.1.0 +- package: k8s.io/client-go + version: ^v1.5.0 +- package: github.com/gogo/protobuf + version: 0.3 - package: github.com/ArthurHlt/go-eureka-client subpackages: - eureka diff --git a/integration/glide.lock b/integration/glide.lock new file mode 100644 index 000000000..038f39076 --- /dev/null +++ b/integration/glide.lock @@ -0,0 +1,295 @@ +hash: c53f57a45247b08a91f127ece494d49f1b7fee8c5f75be87ab12e27aa92d065f +updated: 2016-11-17T16:23:56.727970904Z +imports: +- name: github.com/cenk/backoff + version: 8edc80b07f38c27352fb186d971c628a6c32552b +testImports: +- name: github.com/ArthurHlt/go-eureka-client + version: ba361cd0f9f571b4e871421423d2f02f5689c3d2 + subpackages: + - eureka +- name: github.com/ArthurHlt/gominlog + version: 068c01ce147ad68fca25ef3fa29ae5395ae273ab +- name: github.com/Azure/go-ansiterm + version: fa152c58bc15761d0200cb75fe958b89a9d4888e + subpackages: + - winterm +- name: github.com/boltdb/bolt + version: f4c032d907f61f08dba2d719c58f108a1abb8e81 +- name: github.com/BurntSushi/toml + version: 99064174e013895bbd9b025c31100bd1d9b590ca +- name: github.com/BurntSushi/ty + version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74 + subpackages: + - fun +- name: github.com/cloudfoundry-incubator/candiedyaml + version: 99c3df83b51532e3615f851d8c2dbb638f5313bf +- name: github.com/containous/flaeg + version: a731c034dda967333efce5f8d276aeff11f8ff87 +- name: github.com/containous/staert + version: 92329254783dc01174f03302d51d7cf2c9ff84cf +- name: github.com/containous/traefik + version: 15732269da23c35524bf7cabea5857e4c5f63881 + subpackages: + - autogen + - cluster + - job + - log + - provider + - provider/k8s + - safe + - types + - version +- name: github.com/coreos/etcd + version: c400d05d0aa73e21e431c16145e558d624098018 + subpackages: + - Godeps/_workspace/src/github.com/ugorji/go/codec + - Godeps/_workspace/src/golang.org/x/net/context + - client + - pkg/pathutil + - pkg/types +- name: github.com/daviddengcn/go-colortext + version: 3b18c8575a432453d41fdafb340099fff5bba2f7 +- name: github.com/docker/distribution + version: 99cb7c0946d2f5a38015443e515dc916295064d7 + subpackages: + - context + - digest + - reference + - registry/api/errcode + - registry/api/v2 + - registry/client + - registry/client/auth + - registry/client/transport + - registry/storage/cache + - registry/storage/cache/memory + - uuid +- name: github.com/docker/docker + version: 534753663161334baba06f13b8efa4cad22b5bc5 + subpackages: + - api/types/backend + - builder + - builder/dockerignore + - cliconfig + - cliconfig/configfile + - daemon/graphdriver + - image + - image/v1 + - layer + - opts + - pkg/archive + - pkg/chrootarchive + - pkg/fileutils + - pkg/gitutils + - pkg/homedir + - pkg/httputils + - pkg/idtools + - pkg/ioutils + - pkg/jsonlog + - pkg/jsonmessage + - pkg/longpath + - pkg/mflag + - pkg/mount + - pkg/namesgenerator + - pkg/plugins + - pkg/plugins/transport + - pkg/pools + - pkg/progress + - pkg/promise + - pkg/random + - pkg/reexec + - pkg/signal + - pkg/stdcopy + - pkg/streamformatter + - pkg/stringid + - pkg/symlink + - pkg/system + - pkg/tarsum + - pkg/term + - pkg/term/windows + - pkg/urlutil + - reference + - registry + - runconfig/opts +- name: github.com/docker/engine-api + version: 62043eb79d581a32ea849645277023c550732e52 + subpackages: + - client + - client/transport + - client/transport/cancellable + - types + - types/blkiodev + - types/container + - types/events + - types/filters + - types/network + - types/reference + - types/registry + - types/strslice + - types/swarm + - types/time + - types/versions +- name: github.com/docker/go-connections + version: 988efe982fdecb46f01d53465878ff1f2ff411ce + subpackages: + - nat + - sockets + - tlsconfig +- name: github.com/docker/go-units + version: f2145db703495b2e525c59662db69a7344b00bb8 +- name: github.com/docker/leadership + version: 0a913e2d71a12fd14a028452435cb71ac8d82cb6 +- name: github.com/docker/libcompose + version: d1876c1d68527a49c0aac22a0b161acc7296b740 + subpackages: + - config + - docker + - docker/builder + - docker/client + - docker/network + - labels + - logger + - lookup + - project + - project/events + - project/options + - utils + - version + - yaml +- name: github.com/docker/libkv + version: 3fce6a0f26e07da3eac45796a8e255547a47a750 + subpackages: + - store + - store/boltdb + - store/consul + - store/etcd + - store/zookeeper +- name: github.com/donovanhide/eventsource + version: fd1de70867126402be23c306e1ce32828455d85b +- name: github.com/flynn/go-shlex + version: 3f9db97f856818214da2e1057f8ad84803971cff +- name: github.com/gambol99/go-marathon + version: a558128c87724cd7430060ef5aedf39f83937f55 +- name: github.com/go-check/check + version: 11d3bc7aa68e238947792f30573146a3231fc0f1 +- name: github.com/gogo/protobuf + version: 43ab7f0ec7b6d072e0368bd537ffefe74ed30198 + subpackages: + - proto +- name: github.com/golang/glog + version: fca8c8854093a154ff1eb580aae10276ad6b1b5f +- name: github.com/google/go-querystring + version: 9235644dd9e52eeae6fa48efd539fdc351a0af53 + subpackages: + - query +- name: github.com/gorilla/context + version: 14f550f51af52180c2eefed15e5fd18d63c0a64a +- name: github.com/gorilla/mux + version: e444e69cbd2e2e3e0749a2f3c717cec491552bbf +- name: github.com/hashicorp/consul + version: d8e2fb7dd594163e25a89bc52c1a4613f5c5bfb8 + subpackages: + - api +- name: github.com/hashicorp/go-cleanhttp + version: ad28ea4487f05916463e2423a55166280e8254b5 +- name: github.com/hashicorp/serf + version: 598c54895cc5a7b1a24a398d635e8c0ea0959870 + subpackages: + - coordinate +- name: github.com/libkermit/compose + version: cadc5a3b83a15790174bd7fbc75ea2529785e772 + subpackages: + - check +- name: github.com/libkermit/docker + version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7 +- name: github.com/libkermit/docker-check + version: cbe0ef03b3d23070eac4d00ba8828f2cc7f7e5a3 +- name: github.com/mattn/go-shellwords + version: 525bedee691b5a8df547cb5cf9f86b7fb1883e24 +- name: github.com/mesos/mesos-go + version: 068d5470506e3780189fe607af40892814197c5e + subpackages: + - detector + - detector/zoo + - mesosproto + - mesosutil + - upid +- name: github.com/mesosphere/mesos-dns + version: b47dc4c19f215e98da687b15b4c64e70f629bea5 + repo: https://github.com/containous/mesos-dns.git + vcs: git + subpackages: + - detect + - errorutil + - logging + - models + - records + - records/labels + - records/state + - util +- name: github.com/Microsoft/go-winio + version: ce2922f643c8fd76b46cadc7f404a06282678b34 +- name: github.com/miekg/dns + version: 5d001d020961ae1c184f9f8152fdc73810481677 +- name: github.com/mitchellh/mapstructure + version: ca63d7c062ee3c9f34db231e352b60012b4fd0c1 +- name: github.com/moul/http2curl + version: b1479103caacaa39319f75e7f57fc545287fca0d +- name: github.com/ogier/pflag + version: 45c278ab3607870051a2ea9040bb85fcb8557481 +- name: github.com/opencontainers/runc + version: ba1568de399395774ad84c2ace65937814c542ed + subpackages: + - libcontainer/user +- name: github.com/parnurzeal/gorequest + version: e30af16d4e485943aab0b0885ad6bdbb8c0d3dc7 +- name: github.com/ryanuber/go-glob + version: 572520ed46dbddaed19ea3d9541bdd0494163693 +- name: github.com/samuel/go-zookeeper + version: 87e1bca4477a3cc767ca71be023ced183d74e538 + subpackages: + - zk +- name: github.com/satori/go.uuid + version: 879c5887cd475cd7864858769793b2ceb0d44feb +- name: github.com/Sirupsen/logrus + version: 3ec0642a7fb6488f65b06f9040adc67e3990296a +- name: github.com/spf13/pflag + version: 5644820622454e71517561946e3d94b9f9db6842 +- name: github.com/stretchr/objx + version: cbeaeb16a013161a98496fad62933b1d21786672 +- name: github.com/stretchr/testify + version: b8dc1cecf15bdaf1988d9e87aa7cd98d899a06d6 + subpackages: + - assert + - mock +- name: github.com/tv42/zbase32 + version: 03389da7e0bf9844767f82690f4d68fc097a1306 +- name: github.com/vbatts/tar-split + version: bd4c5d64c3e9297f410025a3b1bd0c58f659e721 + subpackages: + - archive/tar + - tar/asm + - tar/storage +- name: github.com/vdemeester/docker-events + version: be74d4929ec1ad118df54349fda4b0cba60f849b +- name: github.com/vdemeester/shakers + version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 +- name: github.com/xeipuuv/gojsonpointer + version: e0fe6f68307607d540ed8eac07a342c33fa1b54a +- name: github.com/xeipuuv/gojsonreference + version: e02fc20de94c78484cd5ffb007f8af96be030a45 +- name: github.com/xeipuuv/gojsonschema + version: 00f9fafb54d2244d291b86ab63d12c38bd5c3886 +- name: golang.org/x/net + version: db8e4de5b2d6653f66aea53094624468caad15d2 + subpackages: + - context + - proxy + - publicsuffix +- name: golang.org/x/sys + version: 9c60d1c508f5134d1ca726b4641db998f2523357 + subpackages: + - unix + - windows +- name: gopkg.in/fsnotify.v1 + version: 944cff21b3baf3ced9a880365682152ba577d348 diff --git a/integration/glide.yaml b/integration/glide.yaml new file mode 100644 index 000000000..d434ac336 --- /dev/null +++ b/integration/glide.yaml @@ -0,0 +1,33 @@ +package: github.com/containous/traefik/integration +import: +- package: github.com/cenk/backoff +testImport: +- package: github.com/containous/staert + version: 92329254783dc01174f03302d51d7cf2c9ff84cf +- package: github.com/docker/docker + version: 534753663161334baba06f13b8efa4cad22b5bc5 + subpackages: + - pkg/namesgenerator +- package: github.com/docker/libkv + subpackages: + - store + - store/consul + - store/etcd +- package: github.com/go-check/check +- package: github.com/hashicorp/consul + subpackages: + - api +- package: github.com/libkermit/compose + version: cadc5a3b83a15790174bd7fbc75ea2529785e772 + subpackages: + - check +- package: github.com/libkermit/docker + version: 55e3595409924fcfbb850811e5a7cdbe8960a0b7 +- package: github.com/libkermit/docker-check +- package: github.com/mattn/go-shellwords +- package: github.com/vdemeester/shakers +- package: golang.org/x/net + subpackages: + - context +- package: github.com/spf13/pflag + version: 5644820622454e71517561946e3d94b9f9db6842 diff --git a/provider/k8s/client.go b/provider/k8s/client.go index ad9679cae..14420b74f 100644 --- a/provider/k8s/client.go +++ b/provider/k8s/client.go @@ -1,296 +1,268 @@ package k8s import ( - "crypto/tls" - "crypto/x509" - "encoding/json" - "fmt" - "github.com/containous/traefik/log" - "github.com/parnurzeal/gorequest" - "net/http" - "net/url" - "strings" + "time" + + "k8s.io/client-go/1.5/kubernetes" + "k8s.io/client-go/1.5/pkg/api" + "k8s.io/client-go/1.5/pkg/api/v1" + "k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1" + "k8s.io/client-go/1.5/pkg/fields" + "k8s.io/client-go/1.5/pkg/labels" + "k8s.io/client-go/1.5/pkg/runtime" + "k8s.io/client-go/1.5/pkg/watch" + "k8s.io/client-go/1.5/rest" + "k8s.io/client-go/1.5/tools/cache" ) -const ( - // APIEndpoint defines the base path for kubernetes API resources. - APIEndpoint = "/api/v1" - extentionsEndpoint = "/apis/extensions/v1beta1" - defaultIngress = "/ingresses" - namespaces = "/namespaces/" -) +const resyncPeriod = time.Minute * 5 // Client is a client for the Kubernetes master. +// WatchAll starts the watch of the Kubernetes ressources and updates the stores. +// The stores can then be accessed via the Get* functions. type Client interface { - GetIngresses(labelSelector string, predicate func(Ingress) bool) ([]Ingress, error) - GetService(name, namespace string) (Service, error) - GetEndpoints(name, namespace string) (Endpoints, error) - WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error) + GetIngresses(namespaces Namespaces) []*v1beta1.Ingress + GetService(namespace, name string) (*v1.Service, bool, error) + GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error) + WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, error) } type clientImpl struct { - endpointURL string - tls *tls.Config - token string - caCert []byte + ingController *cache.Controller + svcController *cache.Controller + epController *cache.Controller + + ingStore cache.Store + svcStore cache.Store + epStore cache.Store + + clientset *kubernetes.Clientset } -// NewClient returns a new Kubernetes client. -// The provided host is an url (scheme://hostname[:port]) of a -// Kubernetes master without any path. -// The provided client is an authorized http.Client used to perform requests to the Kubernetes API master. -func NewClient(baseURL string, caCert []byte, token string) (Client, error) { - validURL, err := url.Parse(baseURL) +// NewInClusterClient returns a new Kubernetes client that expect to run inside the cluster +func NewInClusterClient() (Client, error) { + config, err := rest.InClusterConfig() if err != nil { - return nil, fmt.Errorf("failed to parse URL %q: %v", baseURL, err) + return nil, err } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + return &clientImpl{ - endpointURL: strings.TrimSuffix(validURL.String(), "/"), - token: token, - caCert: caCert, + clientset: clientset, }, nil } -func makeQueryString(baseParams map[string]string, labelSelector string) (string, error) { - if labelSelector != "" { - baseParams["labelSelector"] = labelSelector - } - queryData, err := json.Marshal(baseParams) +// NewInClusterClientWithEndpoint is the same as NewInClusterClient but uses the provided endpoint URL +func NewInClusterClientWithEndpoint(endpoint string) (Client, error) { + config, err := rest.InClusterConfig() if err != nil { - return "", err + return nil, err } - return string(queryData), nil + + config.Host = endpoint + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + + return &clientImpl{ + clientset: clientset, + }, nil } // GetIngresses returns all ingresses in the cluster -func (c *clientImpl) GetIngresses(labelSelector string, predicate func(Ingress) bool) ([]Ingress, error) { - getURL := c.endpointURL + extentionsEndpoint + defaultIngress - queryParams := map[string]string{} - queryData, err := makeQueryString(queryParams, labelSelector) - if err != nil { - return nil, fmt.Errorf("Had problems constructing query string %s : %v", queryParams, err) - } - body, err := c.do(c.request(getURL, queryData)) - if err != nil { - return nil, fmt.Errorf("failed to create ingresses request: GET %q : %v", getURL, err) - } +func (c *clientImpl) GetIngresses(namespaces Namespaces) []*v1beta1.Ingress { + ingList := c.ingStore.List() + result := make([]*v1beta1.Ingress, 0, len(ingList)) - var ingressList IngressList - if err := json.Unmarshal(body, &ingressList); err != nil { - return nil, fmt.Errorf("failed to decode list of ingress resources: %v", err) - } - ingresses := ingressList.Items[:0] - for _, ingress := range ingressList.Items { - if predicate(ingress) { - ingresses = append(ingresses, ingress) + for _, obj := range ingList { + ingress := obj.(*v1beta1.Ingress) + if HasNamespace(ingress, namespaces) { + result = append(result, ingress) } } - return ingresses, nil + + return result } -// WatchIngresses returns all ingresses in the cluster -func (c *clientImpl) WatchIngresses(labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error) { - getURL := c.endpointURL + extentionsEndpoint + defaultIngress - return c.watch(getURL, labelSelector, stopCh) +// WatchIngresses starts the watch of Kubernetes Ingresses resources and updates the corresponding store +func (c *clientImpl) WatchIngresses(labelSelector labels.Selector, stopCh <-chan struct{}) chan interface{} { + watchCh := make(chan interface{}, 10) + + source := NewListWatchFromClient( + c.clientset.ExtensionsClient, + "ingresses", + api.NamespaceAll, + fields.Everything(), + labelSelector) + + c.ingStore, c.ingController = cache.NewInformer( + source, + &v1beta1.Ingress{}, + resyncPeriod, + newResourceEventHandlerFuncs(watchCh)) + go c.ingController.Run(stopCh) + + return watchCh +} + +func newResourceEventHandlerFuncs(events chan interface{}) cache.ResourceEventHandlerFuncs { + + return cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { events <- obj }, + UpdateFunc: func(old, new interface{}) { events <- new }, + DeleteFunc: func(obj interface{}) { events <- obj }, + } + } // GetService returns the named service from the named namespace -func (c *clientImpl) GetService(name, namespace string) (Service, error) { - getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/services/" + name - - body, err := c.do(c.request(getURL, "")) - if err != nil { - return Service{}, fmt.Errorf("failed to create services request: GET %q : %v", getURL, err) +func (c *clientImpl) GetService(namespace, name string) (*v1.Service, bool, error) { + var service *v1.Service + item, exists, err := c.svcStore.GetByKey(namespace + "/" + name) + if item != nil { + service = item.(*v1.Service) } - var service Service - if err := json.Unmarshal(body, &service); err != nil { - return Service{}, fmt.Errorf("failed to decode service resource: %v", err) - } - return service, nil + return service, exists, err } -// WatchServices returns all services in the cluster -func (c *clientImpl) WatchServices(stopCh <-chan bool) (chan interface{}, chan error, error) { - getURL := c.endpointURL + APIEndpoint + "/services" - return c.watch(getURL, "", stopCh) +// WatchServices starts the watch of Kubernetes Service resources and updates the corresponding store +func (c *clientImpl) WatchServices(stopCh <-chan struct{}) chan interface{} { + watchCh := make(chan interface{}, 10) + + source := cache.NewListWatchFromClient( + c.clientset.CoreClient, + "services", + api.NamespaceAll, + fields.Everything()) + + c.svcStore, c.svcController = cache.NewInformer( + source, + &v1.Service{}, + resyncPeriod, + newResourceEventHandlerFuncs(watchCh)) + go c.svcController.Run(stopCh) + + return watchCh } // GetEndpoints returns the named Endpoints // Endpoints have the same name as the coresponding service -func (c *clientImpl) GetEndpoints(name, namespace string) (Endpoints, error) { - getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/endpoints/" + name +func (c *clientImpl) GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error) { + var endpoint *v1.Endpoints + item, exists, err := c.epStore.GetByKey(namespace + "/" + name) - body, err := c.do(c.request(getURL, "")) - if err != nil { - return Endpoints{}, fmt.Errorf("failed to create endpoints request: GET %q : %v", getURL, err) + if item != nil { + endpoint = item.(*v1.Endpoints) } - var endpoints Endpoints - if err := json.Unmarshal(body, &endpoints); err != nil { - return Endpoints{}, fmt.Errorf("failed to decode endpoints resources: %v", err) - } - return endpoints, nil + return endpoint, exists, err } -// WatchEndpoints returns endpoints in the cluster -func (c *clientImpl) WatchEndpoints(stopCh <-chan bool) (chan interface{}, chan error, error) { - getURL := c.endpointURL + APIEndpoint + "/endpoints" - return c.watch(getURL, "", stopCh) -} - -// WatchAll returns events in the cluster -func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error) { +// WatchEndpoints starts the watch of Kubernetes Endpoints resources and updates the corresponding store +func (c *clientImpl) WatchEndpoints(stopCh <-chan struct{}) chan interface{} { watchCh := make(chan interface{}, 10) - errCh := make(chan error, 10) - stopIngresses := make(chan bool) - chanIngresses, chanIngressesErr, err := c.WatchIngresses(labelSelector, stopIngresses) + source := cache.NewListWatchFromClient( + c.clientset.CoreClient, + "endpoints", + api.NamespaceAll, + fields.Everything()) + + c.epStore, c.epController = cache.NewInformer( + source, + &v1.Endpoints{}, + resyncPeriod, + newResourceEventHandlerFuncs(watchCh)) + go c.epController.Run(stopCh) + + return watchCh +} + +// WatchAll returns events in the cluster and updates the stores via informer +// Filters ingresses by labelSelector +func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, error) { + watchCh := make(chan interface{}, 100) + stopWatchCh := make(chan struct{}, 1) + + kubeLabelSelector, err := labels.Parse(labelSelector) if err != nil { - return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err) - } - stopServices := make(chan bool) - chanServices, chanServicesErr, err := c.WatchServices(stopServices) - if err != nil { - return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err) - } - stopEndpoints := make(chan bool) - chanEndpoints, chanEndpointsErr, err := c.WatchEndpoints(stopEndpoints) - if err != nil { - return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err) + return nil, err } + + chanIngresses := c.WatchIngresses(kubeLabelSelector, stopWatchCh) + chanServices := c.WatchServices(stopWatchCh) + chanEndpoints := c.WatchEndpoints(stopWatchCh) + go func() { + defer close(stopWatchCh) defer close(watchCh) - defer close(errCh) - defer close(stopIngresses) - defer close(stopServices) - defer close(stopEndpoints) for { select { case <-stopCh: - stopIngresses <- true - stopServices <- true - stopEndpoints <- true return - case err := <-chanIngressesErr: - errCh <- err - case err := <-chanServicesErr: - errCh <- err - case err := <-chanEndpointsErr: - errCh <- err case event := <-chanIngresses: - watchCh <- event + c.fireEvent(event, watchCh) case event := <-chanServices: - watchCh <- event + c.fireEvent(event, watchCh) case event := <-chanEndpoints: - watchCh <- event + c.fireEvent(event, watchCh) } } }() - return watchCh, errCh, nil + return watchCh, nil } -func (c *clientImpl) do(request *gorequest.SuperAgent) ([]byte, error) { - res, body, errs := request.EndBytes() - if errs != nil { - return nil, fmt.Errorf("failed to create request: GET %q : %v", request.Url, errs) +// fireEvent checks if all controllers have synced before firing +// Used after startup or a reconnect +func (c *clientImpl) fireEvent(event interface{}, watchCh chan interface{}) { + if c.ingController.HasSynced() && c.svcController.HasSynced() && c.epController.HasSynced() { + watchCh <- event } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("http error %d GET %q: %q", res.StatusCode, request.Url, string(body)) - } - return body, nil } -func (c *clientImpl) request(reqURL string, queryContent interface{}) *gorequest.SuperAgent { - // Make request to Kubernetes API - parsedURL, parseErr := url.Parse(reqURL) - if parseErr != nil { - log.Errorf("Had issues parsing url %s. Trying anyway.", reqURL) +// HasNamespace checks if the ingress is in one of the namespaces +func HasNamespace(ingress *v1beta1.Ingress, namespaces Namespaces) bool { + if len(namespaces) == 0 { + return true } - request := gorequest.New().Get(reqURL) - request.Transport.DisableKeepAlives = true - - if parsedURL.Scheme == "https" { - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(c.caCert) - c.tls = &tls.Config{RootCAs: pool} - request.TLSClientConfig(c.tls) - } - if len(c.token) > 0 { - request.Header["Authorization"] = "Bearer " + c.token - } - request.Query(queryContent) - return request -} - -// GenericObject generic object -type GenericObject struct { - TypeMeta `json:",inline"` - ListMeta `json:"metadata,omitempty"` -} - -func (c *clientImpl) watch(url string, labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error) { - watchCh := make(chan interface{}, 10) - errCh := make(chan error, 10) - - // get version - body, err := c.do(c.request(url, "")) - if err != nil { - return watchCh, errCh, fmt.Errorf("failed to do version request: GET %q : %v", url, err) - } - - var generic GenericObject - if err := json.Unmarshal(body, &generic); err != nil { - return watchCh, errCh, fmt.Errorf("failed to decode version %v", err) - } - resourceVersion := generic.ResourceVersion - queryParams := map[string]string{"watch": "", "resourceVersion": resourceVersion} - queryData, err := makeQueryString(queryParams, labelSelector) - if err != nil { - return watchCh, errCh, fmt.Errorf("Unable to construct query args") - } - request := c.request(url, queryData) - req, err := request.MakeRequest() - if err != nil { - return watchCh, errCh, fmt.Errorf("failed to make watch request: GET %q : %v", url, err) - } - request.Client.Transport = request.Transport - - res, err := request.Client.Do(req) - if err != nil { - return watchCh, errCh, fmt.Errorf("failed to do watch request: GET %q: %v", url, err) - } - - go func() { - finishCh := make(chan bool) - defer close(finishCh) - defer close(watchCh) - defer close(errCh) - go func() { - defer res.Body.Close() - for { - var eventList interface{} - if err := json.NewDecoder(res.Body).Decode(&eventList); err != nil { - if !strings.Contains(err.Error(), "net/http: request canceled") { - errCh <- fmt.Errorf("failed to decode watch event: GET %q : %v", url, err) - } - finishCh <- true - return - } - watchCh <- eventList - } - }() - select { - case <-stopCh: - go func() { - request.Transport.CancelRequest(req) - }() - <-finishCh - return + for _, n := range namespaces { + if ingress.ObjectMeta.Namespace == n { + return true } - }() - return watchCh, errCh, nil + } + return false +} + +// NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace, field selector and label selector. +// Extends cache.NewListWatchFromClient to support labelSelector +func NewListWatchFromClient(c cache.Getter, resource string, namespace string, fieldSelector fields.Selector, labelSelector labels.Selector) *cache.ListWatch { + listFunc := func(options api.ListOptions) (runtime.Object, error) { + return c.Get(). + Namespace(namespace). + Resource(resource). + VersionedParams(&options, api.ParameterCodec). + FieldsSelectorParam(fieldSelector). + LabelsSelectorParam(labelSelector). + Do(). + Get() + } + watchFunc := func(options api.ListOptions) (watch.Interface, error) { + return c.Get(). + Prefix("watch"). + Namespace(namespace). + Resource(resource). + VersionedParams(&options, api.ParameterCodec). + FieldsSelectorParam(fieldSelector). + LabelsSelectorParam(labelSelector). + Watch() + } + return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc} } diff --git a/provider/k8s/endpoints.go b/provider/k8s/endpoints.go deleted file mode 100644 index 123ffe36c..000000000 --- a/provider/k8s/endpoints.go +++ /dev/null @@ -1,84 +0,0 @@ -package k8s - -// Endpoints is a collection of endpoints that implement the actual service. Example: -// Name: "mysvc", -// Subsets: [ -// { -// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}], -// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}] -// }, -// { -// Addresses: [{"ip": "10.10.3.3"}], -// Ports: [{"name": "a", "port": 93}, {"name": "b", "port": 76}] -// }, -// ] -type Endpoints struct { - TypeMeta `json:",inline"` - ObjectMeta `json:"metadata,omitempty"` - - // The set of all endpoints is the union of all subsets. - Subsets []EndpointSubset -} - -// EndpointSubset is a group of addresses with a common set of ports. The -// expanded set of endpoints is the Cartesian product of Addresses x Ports. -// For example, given: -// { -// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}], -// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}] -// } -// The resulting set of endpoints can be viewed as: -// a: [ 10.10.1.1:8675, 10.10.2.2:8675 ], -// b: [ 10.10.1.1:309, 10.10.2.2:309 ] -type EndpointSubset struct { - Addresses []EndpointAddress - NotReadyAddresses []EndpointAddress - Ports []EndpointPort -} - -// EndpointAddress is a tuple that describes single IP address. -type EndpointAddress struct { - // The IP of this endpoint. - // IPv6 is also accepted but not fully supported on all platforms. Also, certain - // kubernetes components, like kube-proxy, are not IPv6 ready. - // TODO: This should allow hostname or IP, see #4447. - IP string - // Optional: Hostname of this endpoint - // Meant to be used by DNS servers etc. - Hostname string `json:"hostname,omitempty"` - // Optional: The kubernetes object related to the entry point. - TargetRef *ObjectReference -} - -// EndpointPort is a tuple that describes a single port. -type EndpointPort struct { - // The name of this port (corresponds to ServicePort.Name). Optional - // if only one port is defined. Must be a DNS_LABEL. - Name string - - // The port number. - Port int32 - - // The IP protocol for this port. - Protocol Protocol -} - -// ObjectReference contains enough information to let you inspect or modify the referred object. -type ObjectReference struct { - Kind string `json:"kind,omitempty"` - Namespace string `json:"namespace,omitempty"` - Name string `json:"name,omitempty"` - UID UID `json:"uid,omitempty"` - APIVersion string `json:"apiVersion,omitempty"` - ResourceVersion string `json:"resourceVersion,omitempty"` - - // Optional. If referring to a piece of an object instead of an entire object, this string - // should contain information to identify the sub-object. For example, if the object - // reference is to a container within a pod, this would take on a value like: - // "spec.containers{name}" (where "name" refers to the name of the container that triggered - // the event) or if no container name is specified "spec.containers[2]" (container with - // index 2 in this pod). This syntax is chosen only to have some well-defined way of - // referencing a part of an object. - // TODO: this design is not final and this field is subject to change in the future. - FieldPath string `json:"fieldPath,omitempty"` -} diff --git a/provider/k8s/ingress.go b/provider/k8s/ingress.go deleted file mode 100644 index f3b7c8dce..000000000 --- a/provider/k8s/ingress.go +++ /dev/null @@ -1,151 +0,0 @@ -package k8s - -// Ingress is a collection of rules that allow inbound connections to reach the -// endpoints defined by a backend. An Ingress can be configured to give services -// externally-reachable urls, load balance traffic, terminate SSL, offer name -// based virtual hosting etc. -type Ingress struct { - TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata - ObjectMeta `json:"metadata,omitempty"` - - // Spec is the desired state of the Ingress. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status - Spec IngressSpec `json:"spec,omitempty"` - - // Status is the current state of the Ingress. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status - Status IngressStatus `json:"status,omitempty"` -} - -// IngressList is a collection of Ingress. -type IngressList struct { - TypeMeta `json:",inline"` - // Standard object's metadata. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata - ListMeta `json:"metadata,omitempty"` - - // Items is the list of Ingress. - Items []Ingress `json:"items"` -} - -// IngressSpec describes the Ingress the user wishes to exist. -type IngressSpec struct { - // A default backend capable of servicing requests that don't match any - // rule. At least one of 'backend' or 'rules' must be specified. This field - // is optional to allow the loadbalancer controller or defaulting logic to - // specify a global default. - Backend *IngressBackend `json:"backend,omitempty"` - - // TLS configuration. Currently the Ingress only supports a single TLS - // port, 443. If multiple members of this list specify different hosts, they - // will be multiplexed on the same port according to the hostname specified - // through the SNI TLS extension, if the ingress controller fulfilling the - // ingress supports SNI. - TLS []IngressTLS `json:"tls,omitempty"` - - // A list of host rules used to configure the Ingress. If unspecified, or - // no rule matches, all traffic is sent to the default backend. - Rules []IngressRule `json:"rules,omitempty"` - // TODO: Add the ability to specify load-balancer IP through claims -} - -// IngressTLS describes the transport layer security associated with an Ingress. -type IngressTLS struct { - // Hosts are a list of hosts included in the TLS certificate. The values in - // this list must match the name/s used in the tlsSecret. Defaults to the - // wildcard host setting for the loadbalancer controller fulfilling this - // Ingress, if left unspecified. - Hosts []string `json:"hosts,omitempty"` - // SecretName is the name of the secret used to terminate SSL traffic on 443. - // Field is left optional to allow SSL routing based on SNI hostname alone. - // If the SNI host in a listener conflicts with the "Host" header field used - // by an IngressRule, the SNI host is used for termination and value of the - // Host header is used for routing. - SecretName string `json:"secretName,omitempty"` - // TODO: Consider specifying different modes of termination, protocols etc. -} - -// IngressStatus describe the current state of the Ingress. -type IngressStatus struct { - // LoadBalancer contains the current status of the load-balancer. - LoadBalancer LoadBalancerStatus `json:"loadBalancer,omitempty"` -} - -// IngressRule represents the rules mapping the paths under a specified host to -// the related backend services. Incoming requests are first evaluated for a host -// match, then routed to the backend associated with the matching IngressRuleValue. -type IngressRule struct { - // Host is the fully qualified domain name of a network host, as defined - // by RFC 3986. Note the following deviations from the "host" part of the - // URI as defined in the RFC: - // 1. IPs are not allowed. Currently an IngressRuleValue can only apply to the - // IP in the Spec of the parent Ingress. - // 2. The `:` delimiter is not respected because ports are not allowed. - // Currently the port of an Ingress is implicitly :80 for http and - // :443 for https. - // Both these may change in the future. - // Incoming requests are matched against the host before the IngressRuleValue. - // If the host is unspecified, the Ingress routes all traffic based on the - // specified IngressRuleValue. - Host string `json:"host,omitempty"` - // IngressRuleValue represents a rule to route requests for this IngressRule. - // If unspecified, the rule defaults to a http catch-all. Whether that sends - // just traffic matching the host to the default backend or all traffic to the - // default backend, is left to the controller fulfilling the Ingress. Http is - // currently the only supported IngressRuleValue. - IngressRuleValue `json:",inline,omitempty"` -} - -// IngressRuleValue represents a rule to apply against incoming requests. If the -// rule is satisfied, the request is routed to the specified backend. Currently -// mixing different types of rules in a single Ingress is disallowed, so exactly -// one of the following must be set. -type IngressRuleValue struct { - //TODO: - // 1. Consider renaming this resource and the associated rules so they - // aren't tied to Ingress. They can be used to route intra-cluster traffic. - // 2. Consider adding fields for ingress-type specific global options - // usable by a loadbalancer, like http keep-alive. - - HTTP *HTTPIngressRuleValue `json:"http,omitempty"` -} - -// HTTPIngressRuleValue is a list of http selectors pointing to backends. -// In the example: http:///? -> backend where -// where parts of the url correspond to RFC 3986, this resource will be used -// to match against everything after the last '/' and before the first '?' -// or '#'. -type HTTPIngressRuleValue struct { - // A collection of paths that map requests to backends. - Paths []HTTPIngressPath `json:"paths"` - // TODO: Consider adding fields for ingress-type specific global - // options usable by a loadbalancer, like http keep-alive. -} - -// HTTPIngressPath associates a path regex with a backend. Incoming urls matching -// the path are forwarded to the backend. -type HTTPIngressPath struct { - // Path is a extended POSIX regex as defined by IEEE Std 1003.1, - // (i.e this follows the egrep/unix syntax, not the perl syntax) - // matched against the path of an incoming request. Currently it can - // contain characters disallowed from the conventional "path" - // part of a URL as defined by RFC 3986. Paths must begin with - // a '/'. If unspecified, the path defaults to a catch all sending - // traffic to the backend. - Path string `json:"path,omitempty"` - - // Backend defines the referenced service endpoint to which the traffic - // will be forwarded to. - Backend IngressBackend `json:"backend"` -} - -// IngressBackend describes all endpoints for a given service and port. -type IngressBackend struct { - // Specifies the name of the referenced service. - ServiceName string `json:"serviceName"` - - // Specifies the port of the referenced service. - ServicePort IntOrString `json:"servicePort"` -} diff --git a/provider/k8s/namespace.go b/provider/k8s/namespace.go new file mode 100644 index 000000000..6f458a7d8 --- /dev/null +++ b/provider/k8s/namespace.go @@ -0,0 +1,32 @@ +package k8s + +import ( + "fmt" + "strings" +) + +// Namespaces holds kubernetes namespaces +type Namespaces []string + +//Set adds strings elem into the the parser +//it splits str on , and ; +func (ns *Namespaces) Set(str string) error { + fargs := func(c rune) bool { + return c == ',' || c == ';' + } + // get function + slice := strings.FieldsFunc(str, fargs) + *ns = append(*ns, slice...) + return nil +} + +//Get []string +func (ns *Namespaces) Get() interface{} { return Namespaces(*ns) } + +//String return slice in a string +func (ns *Namespaces) String() string { return fmt.Sprintf("%v", *ns) } + +//SetValue sets []string into the parser +func (ns *Namespaces) SetValue(val interface{}) { + *ns = Namespaces(val.(Namespaces)) +} diff --git a/provider/k8s/service.go b/provider/k8s/service.go deleted file mode 100644 index e501718ce..000000000 --- a/provider/k8s/service.go +++ /dev/null @@ -1,326 +0,0 @@ -package k8s - -import ( - "encoding/json" - "strconv" - "time" -) - -// TypeMeta describes an individual object in an API response or request -// with strings representing the type of the object and its API schema version. -// Structures that are versioned or persisted should inline TypeMeta. -type TypeMeta struct { - // 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: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds - Kind string `json:"kind,omitempty"` - - // 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: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources - APIVersion string `json:"apiVersion,omitempty"` -} - -// ObjectMeta is metadata that all persisted resources must have, which includes all objects -// users must create. -type ObjectMeta struct { - // Name is unique within a namespace. Name is required when creating resources, although - // some resources may allow a client to request the generation of an appropriate name - // automatically. Name is primarily intended for creation idempotence and configuration - // definition. - Name string `json:"name,omitempty"` - - // GenerateName indicates that the name should be made unique by the server prior to persisting - // it. A non-empty value for the field indicates the name will be made unique (and the name - // returned to the client will be different than the name passed). The value of this field will - // be combined with a unique suffix on the server if the Name field has not been provided. - // The provided value must be valid within the rules for Name, and may be truncated by the length - // of the suffix required to make the value unique on the server. - // - // If this field is specified, and Name is not present, the server will NOT return a 409 if the - // generated name exists - instead, it will either return 201 Created or 500 with Reason - // ServerTimeout indicating a unique name could not be found in the time allotted, and the client - // should retry (optionally after the time indicated in the Retry-After header). - GenerateName string `json:"generateName,omitempty"` - - // Namespace defines the space within which name must be unique. An empty namespace is - // equivalent to the "default" namespace, but "default" is the canonical representation. - // Not all objects are required to be scoped to a namespace - the value of this field for - // those objects will be empty. - Namespace string `json:"namespace,omitempty"` - - // SelfLink is a URL representing this object. - SelfLink string `json:"selfLink,omitempty"` - - // UID is the unique in time and space value for this object. It is typically generated by - // the server on successful creation of a resource and is not allowed to change on PUT - // operations. - UID UID `json:"uid,omitempty"` - - // An opaque value that represents the version of this resource. May be used for optimistic - // concurrency, change detection, and the watch operation on a resource or set of resources. - // Clients must treat these values as opaque and values may only be valid for a particular - // resource or set of resources. Only servers will generate resource versions. - ResourceVersion string `json:"resourceVersion,omitempty"` - - // A sequence number representing a specific generation of the desired state. - // Populated by the system. Read-only. - Generation int64 `json:"generation,omitempty"` - - // CreationTimestamp is a timestamp representing the server time when this object was - // created. It is not guaranteed to be set in happens-before order across separate operations. - // Clients may not set this value. It is represented in RFC3339 form and is in UTC. - CreationTimestamp Time `json:"creationTimestamp,omitempty"` - - // DeletionTimestamp is the time after which this resource will be deleted. This - // field is set by the server when a graceful deletion is requested by the user, and is not - // directly settable by a client. The resource will be deleted (no longer visible from - // resource lists, and not reachable by name) after the time in this field. Once set, this - // value may not be unset or be set further into the future, although it may be shortened - // or the resource may be deleted prior to this time. For example, a user may request that - // a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination - // signal to the containers in the pod. Once the resource is deleted in the API, the Kubelet - // will send a hard termination signal to the container. - DeletionTimestamp *Time `json:"deletionTimestamp,omitempty"` - - // DeletionGracePeriodSeconds records the graceful deletion value set when graceful deletion - // was requested. Represents the most recent grace period, and may only be shortened once set. - DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty"` - - // Labels are key value pairs that may be used to scope and select individual resources. - // Label keys are of the form: - // label-key ::= prefixed-name | name - // prefixed-name ::= prefix '/' name - // prefix ::= DNS_SUBDOMAIN - // name ::= DNS_LABEL - // The prefix is optional. If the prefix is not specified, the key is assumed to be private - // to the user. Other system components that wish to use labels must specify a prefix. The - // "kubernetes.io/" prefix is reserved for use by kubernetes components. - // TODO: replace map[string]string with labels.LabelSet type - Labels map[string]string `json:"labels,omitempty"` - - // Annotations are unstructured key value data stored with a resource that may be set by - // external tooling. They are not queryable and should be preserved when modifying - // objects. Annotation keys have the same formatting restrictions as Label keys. See the - // comments on Labels for details. - Annotations map[string]string `json:"annotations,omitempty"` -} - -// UID is a type that holds unique ID values, including UUIDs. Because we -// don't ONLY use UUIDs, this is an alias to string. Being a type captures -// intent and helps make sure that UIDs and names do not get conflated. -type UID string - -// Time is a wrapper around time.Time which supports correct -// marshaling to YAML and JSON. Wrappers are provided for many -// of the factory methods that the time package offers. -// -// +protobuf.options.marshal=false -// +protobuf.as=Timestamp -type Time struct { - time.Time `protobuf:"-"` -} - -// Service is a named abstraction of software service (for example, mysql) consisting of local port -// (for example 3306) that the proxy listens on, and the selector that determines which pods -// will answer requests sent through the proxy. -type Service struct { - TypeMeta `json:",inline"` - ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the behavior of a service. - Spec ServiceSpec `json:"spec,omitempty"` - - // Status represents the current status of a service. - Status ServiceStatus `json:"status,omitempty"` -} - -// ServiceSpec describes the attributes that a user creates on a service -type ServiceSpec struct { - // Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer - Type ServiceType `json:"type,omitempty"` - - // Required: The list of ports that are exposed by this service. - Ports []ServicePort `json:"ports"` - - // This service will route traffic to pods having labels matching this selector. If empty or not present, - // the service is assumed to have endpoints set by an external process and Kubernetes will not modify - // those endpoints. - Selector map[string]string `json:"selector"` - - // ClusterIP is usually assigned by the master. If specified by the user - // we will try to respect it or else fail the request. This field can - // not be changed by updates. - // Valid values are None, empty string (""), or a valid IP address - // None can be specified for headless services when proxying is not required - ClusterIP string `json:"clusterIP,omitempty"` - - // ExternalIPs are used by external load balancers, or can be set by - // users to handle external traffic that arrives at a node. - ExternalIPs []string `json:"externalIPs,omitempty"` - - // Only applies to Service Type: LoadBalancer - // LoadBalancer will get created with the IP specified in this field. - // This feature depends on whether the underlying cloud-provider supports specifying - // the loadBalancerIP when a load balancer is created. - // This field will be ignored if the cloud-provider does not support the feature. - LoadBalancerIP string `json:"loadBalancerIP,omitempty"` - - // Required: Supports "ClientIP" and "None". Used to maintain session affinity. - SessionAffinity ServiceAffinity `json:"sessionAffinity,omitempty"` -} - -// ServicePort service port -type ServicePort struct { - // Optional if only one ServicePort is defined on this service: The - // name of this port within the service. This must be a DNS_LABEL. - // All ports within a ServiceSpec must have unique names. This maps to - // the 'Name' field in EndpointPort objects. - Name string `json:"name"` - - // The IP protocol for this port. Supports "TCP" and "UDP". - Protocol Protocol `json:"protocol"` - - // The port that will be exposed on the service. - Port int `json:"port"` - - // Optional: The target port on pods selected by this service. If this - // is a string, it will be looked up as a named port in the target - // Pod's container ports. If this is not specified, the value - // of the 'port' field is used (an identity map). - // This field is ignored for services with clusterIP=None, and should be - // omitted or set equal to the 'port' field. - TargetPort IntOrString `json:"targetPort"` - - // The port on each node on which this service is exposed. - // Default is to auto-allocate a port if the ServiceType of this Service requires one. - NodePort int `json:"nodePort"` -} - -// ServiceStatus represents the current status of a service -type ServiceStatus struct { - // LoadBalancer contains the current status of the load-balancer, - // if one is present. - LoadBalancer LoadBalancerStatus `json:"loadBalancer,omitempty"` -} - -// LoadBalancerStatus represents the status of a load-balancer -type LoadBalancerStatus struct { - // Ingress is a list containing ingress points for the load-balancer; - // traffic intended for the service should be sent to these ingress points. - Ingress []LoadBalancerIngress `json:"ingress,omitempty"` -} - -// LoadBalancerIngress represents the status of a load-balancer ingress point: -// traffic intended for the service should be sent to an ingress point. -type LoadBalancerIngress struct { - // IP is set for load-balancer ingress points that are IP based - // (typically GCE or OpenStack load-balancers) - IP string `json:"ip,omitempty"` - - // Hostname is set for load-balancer ingress points that are DNS based - // (typically AWS load-balancers) - Hostname string `json:"hostname,omitempty"` -} - -// ServiceAffinity Session Affinity Type string -type ServiceAffinity string - -// ServiceType Service Type string describes ingress methods for a service -type ServiceType string - -// Protocol defines network protocols supported for things like container ports. -type Protocol string - -// IntOrString is a type that can hold an int32 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. -// TODO: Rename to Int32OrString -// -// +protobuf=true -// +protobuf.options.(gogoproto.goproto_stringer)=false -type IntOrString struct { - Type Type - IntVal int32 - StrVal string -} - -// FromInt creates an IntOrString object with an int32 value. It is -// your responsibility not to call this method with a value greater -// than int32. -// TODO: convert to (val int32) -func FromInt(val int) IntOrString { - return IntOrString{Type: Int, IntVal: int32(val)} -} - -// FromString creates an IntOrString object with a string value. -func FromString(val string) IntOrString { - return IntOrString{Type: String, StrVal: val} -} - -// String returns the string value, or the Itoa of the int value. -func (intstr *IntOrString) String() string { - if intstr.Type == String { - return intstr.StrVal - } - return strconv.Itoa(intstr.IntValue()) -} - -// IntValue returns the IntVal if type Int, or if -// it is a String, will attempt a conversion to int. -func (intstr *IntOrString) IntValue() int { - if intstr.Type == String { - i, _ := strconv.Atoi(intstr.StrVal) - return i - } - return int(intstr.IntVal) -} - -// UnmarshalJSON implements the json.Unmarshaller interface. -func (intstr *IntOrString) UnmarshalJSON(value []byte) error { - if value[0] == '"' { - intstr.Type = String - return json.Unmarshal(value, &intstr.StrVal) - } - intstr.Type = Int - return json.Unmarshal(value, &intstr.IntVal) -} - -// Type represents the stored type of IntOrString. -type Type int - -const ( - // Int int - Int Type = iota // The IntOrString holds an int. - //String string - String // The IntOrString holds a string. -) - -// ServiceList holds a list of services. -type ServiceList struct { - TypeMeta `json:",inline"` - ListMeta `json:"metadata,omitempty"` - - Items []Service `json:"items"` -} - -// ListMeta describes metadata that synthetic resources must have, including lists and -// various status objects. A resource may have only one of {ObjectMeta, ListMeta}. -type ListMeta struct { - // SelfLink is a URL representing this object. - // Populated by the system. - // Read-only. - SelfLink string `json:"selfLink,omitempty"` - - // String that identifies the server's internal version of this object that - // can be used by clients to determine when objects have changed. - // Value must be treated as opaque by clients and passed unmodified back to the server. - // Populated by the system. - // Read-only. - // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency - ResourceVersion string `json:"resourceVersion,omitempty"` -} diff --git a/provider/kubernetes.go b/provider/kubernetes.go index 3ca6cd681..32c0a6ba2 100644 --- a/provider/kubernetes.go +++ b/provider/kubernetes.go @@ -1,101 +1,49 @@ package provider import ( - "fmt" - "github.com/containous/traefik/log" - "github.com/containous/traefik/provider/k8s" - "github.com/containous/traefik/safe" - "github.com/containous/traefik/types" - "io/ioutil" - "os" "reflect" "strconv" "strings" "text/template" "time" + "k8s.io/client-go/1.5/pkg/api/v1" + "k8s.io/client-go/1.5/pkg/util/intstr" + + "github.com/containous/traefik/log" + "github.com/containous/traefik/provider/k8s" + "github.com/containous/traefik/safe" + "github.com/containous/traefik/types" + "github.com/cenk/backoff" "github.com/containous/traefik/job" ) -const ( - serviceAccountToken = "/var/run/secrets/kubernetes.io/serviceaccount/token" - serviceAccountCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" - defaultKubeEndpoint = "http://127.0.0.1:8080" -) - -// Namespaces holds kubernetes namespaces -type Namespaces []string - -//Set adds strings elem into the the parser -//it splits str on , and ; -func (ns *Namespaces) Set(str string) error { - fargs := func(c rune) bool { - return c == ',' || c == ';' - } - // get function - slice := strings.FieldsFunc(str, fargs) - *ns = append(*ns, slice...) - return nil -} - -//Get []string -func (ns *Namespaces) Get() interface{} { return Namespaces(*ns) } - -//String return slice in a string -func (ns *Namespaces) String() string { return fmt.Sprintf("%v", *ns) } - -//SetValue sets []string into the parser -func (ns *Namespaces) SetValue(val interface{}) { - *ns = Namespaces(val.(Namespaces)) -} - var _ Provider = (*Kubernetes)(nil) // Kubernetes holds configurations of the Kubernetes provider. type Kubernetes struct { BaseProvider `mapstructure:",squash"` - Endpoint string `description:"Kubernetes server endpoint"` - DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"` - Namespaces Namespaces `description:"Kubernetes namespaces"` - LabelSelector string `description:"Kubernetes api label selector to use"` + Endpoint string `description:"Kubernetes server endpoint"` + DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"` + Namespaces k8s.Namespaces `description:"Kubernetes namespaces"` + LabelSelector string `description:"Kubernetes api label selector to use"` lastConfiguration safe.Safe } -func (provider *Kubernetes) createClient() (k8s.Client, error) { - var token string - tokenBytes, err := ioutil.ReadFile(serviceAccountToken) - if err == nil { - token = string(tokenBytes) - log.Debugf("Kubernetes token: %s", token) - } else { - log.Errorf("Kubernetes load token error: %s", err) +func (provider *Kubernetes) newK8sClient() (k8s.Client, error) { + if provider.Endpoint != "" { + log.Infof("Creating in cluster Kubernetes client with endpoint %", provider.Endpoint) + return k8s.NewInClusterClientWithEndpoint(provider.Endpoint) } - caCert, err := ioutil.ReadFile(serviceAccountCACert) - if err == nil { - log.Debugf("Kubernetes CA cert: %s", serviceAccountCACert) - } else { - log.Errorf("Kubernetes load token error: %s", err) - } - kubernetesHost := os.Getenv("KUBERNETES_SERVICE_HOST") - kubernetesPort := os.Getenv("KUBERNETES_SERVICE_PORT_HTTPS") - // Prioritize user provided kubernetes endpoint since kube container runtime will almost always have it - if provider.Endpoint == "" && len(kubernetesPort) > 0 && len(kubernetesHost) > 0 { - log.Debugf("Using environment provided kubernetes endpoint") - provider.Endpoint = "https://" + kubernetesHost + ":" + kubernetesPort - } - if provider.Endpoint == "" { - log.Debugf("Using default kubernetes api endpoint") - provider.Endpoint = defaultKubeEndpoint - } - log.Debugf("Kubernetes endpoint: %s", provider.Endpoint) - return k8s.NewClient(provider.Endpoint, caCert, token) + log.Info("Creating in cluster Kubernetes client") + return k8s.NewInClusterClient() } // Provide allows the provider to provide configurations to traefik // using the given configuration channel. func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error { - k8sClient, err := provider.createClient() + k8sClient, err := provider.newK8sClient() if err != nil { return err } @@ -107,7 +55,7 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage stopWatch := make(chan bool, 5) defer close(stopWatch) log.Debugf("Using label selector: '%s'", provider.LabelSelector) - eventsChan, errEventsChan, err := k8sClient.WatchAll(provider.LabelSelector, stopWatch) + eventsChan, err := k8sClient.WatchAll(provider.LabelSelector, stopWatch) if err != nil { log.Errorf("Error watching kubernetes events: %v", err) timer := time.NewTimer(1 * time.Second) @@ -123,9 +71,6 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage case <-stop: stopWatch <- true return nil - case err, _ := <-errEventsChan: - stopWatch <- true - return err case event := <-eventsChan: log.Debugf("Received event from kubernetes %+v", event) templateObjects, err := provider.loadIngresses(k8sClient) @@ -155,39 +100,12 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage } }) - templateObjects, err := provider.loadIngresses(k8sClient) - if err != nil { - return err - } - if reflect.DeepEqual(provider.lastConfiguration.Get(), templateObjects) { - log.Debugf("Skipping configuration from kubernetes %+v", templateObjects) - } else { - provider.lastConfiguration.Set(templateObjects) - configurationChan <- types.ConfigMessage{ - ProviderName: "kubernetes", - Configuration: provider.loadConfig(*templateObjects), - } - } - return nil } func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configuration, error) { - ingresses, err := k8sClient.GetIngresses(provider.LabelSelector, func(ingress k8s.Ingress) bool { - if len(provider.Namespaces) == 0 { - return true - } - for _, n := range provider.Namespaces { - if ingress.ObjectMeta.Namespace == n { - return true - } - } - return false - }) - if err != nil { - log.Errorf("Error retrieving ingresses: %+v", err) - return nil, err - } + ingresses := k8sClient.GetIngresses(provider.Namespaces) + templateObjects := types.Configuration{ map[string]*types.Backend{}, map[string]*types.Frontend{}, @@ -239,28 +157,28 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur Rule: ruleType + ":" + pa.Path, } } - service, err := k8sClient.GetService(pa.Backend.ServiceName, i.ObjectMeta.Namespace) - if err != nil { - log.Warnf("Error retrieving services: %v", err) + service, exists, err := k8sClient.GetService(i.ObjectMeta.Namespace, pa.Backend.ServiceName) + if err != nil || !exists { + log.Warnf("Error retrieving service %s/%s: %v", i.ObjectMeta.Namespace, pa.Backend.ServiceName, err) delete(templateObjects.Frontends, r.Host+pa.Path) - log.Warnf("Error retrieving services %s", pa.Backend.ServiceName) continue } + protocol := "http" for _, port := range service.Spec.Ports { if equalPorts(port, pa.Backend.ServicePort) { if port.Port == 443 { protocol = "https" } - endpoints, err := k8sClient.GetEndpoints(service.ObjectMeta.Name, service.ObjectMeta.Namespace) - if err != nil { - log.Errorf("Error retrieving endpoints: %v", err) + endpoints, exists, err := k8sClient.GetEndpoints(service.ObjectMeta.Namespace, service.ObjectMeta.Name) + if err != nil || !exists { + log.Errorf("Error retrieving endpoints %s/%s: %v", service.ObjectMeta.Namespace, service.ObjectMeta.Name, err) continue } if len(endpoints.Subsets) == 0 { log.Warnf("Endpoints not found for %s/%s, falling back to Service ClusterIP", service.ObjectMeta.Namespace, service.ObjectMeta.Name) templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{ - URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(port.Port), + URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(int(port.Port)), Weight: 1, } } else { @@ -287,7 +205,7 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur return &templateObjects, nil } -func endpointPortNumber(servicePort k8s.ServicePort, endpointPorts []k8s.EndpointPort) int { +func endpointPortNumber(servicePort v1.ServicePort, endpointPorts []v1.EndpointPort) int { if len(endpointPorts) > 0 { //name is optional if there is only one port port := endpointPorts[0] @@ -298,11 +216,11 @@ func endpointPortNumber(servicePort k8s.ServicePort, endpointPorts []k8s.Endpoin } return int(port.Port) } - return servicePort.Port + return int(servicePort.Port) } -func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool { - if servicePort.Port == ingressPort.IntValue() { +func equalPorts(servicePort v1.ServicePort, ingressPort intstr.IntOrString) bool { + if int(servicePort.Port) == ingressPort.IntValue() { return true } if servicePort.Name != "" && servicePort.Name == ingressPort.String() { diff --git a/provider/kubernetes_test.go b/provider/kubernetes_test.go index 2a4e935f9..4148a72c2 100644 --- a/provider/kubernetes_test.go +++ b/provider/kubernetes_test.go @@ -2,29 +2,34 @@ package provider import ( "encoding/json" - "github.com/containous/traefik/provider/k8s" - "github.com/containous/traefik/types" "reflect" "testing" + + "k8s.io/client-go/1.5/pkg/api/v1" + "k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1" + "k8s.io/client-go/1.5/pkg/util/intstr" + + "github.com/containous/traefik/provider/k8s" + "github.com/containous/traefik/types" ) func TestLoadIngresses(t *testing.T) { - ingresses := []k8s.Ingress{{ - ObjectMeta: k8s.ObjectMeta{ + ingresses := []*v1beta1.Ingress{{ + ObjectMeta: v1.ObjectMeta{ Namespace: "testing", }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(80), + ServicePort: intstr.FromInt(80), }, }, }, @@ -33,19 +38,19 @@ func TestLoadIngresses(t *testing.T) { }, { Host: "bar", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service3", - ServicePort: k8s.FromString("https"), + ServicePort: intstr.FromString("https"), }, }, { - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service2", - ServicePort: k8s.FromInt(802), + ServicePort: intstr.FromInt(802), }, }, }, @@ -55,16 +60,16 @@ func TestLoadIngresses(t *testing.T) { }, }, }} - services := []k8s.Service{ + services := []*v1.Service{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service1", UID: "1", Namespace: "testing", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.1", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Port: 80, }, @@ -72,14 +77,14 @@ func TestLoadIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service2", UID: "2", Namespace: "testing", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.2", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Port: 802, }, @@ -87,14 +92,14 @@ func TestLoadIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service3", UID: "3", Namespace: "testing", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.3", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 80, @@ -107,33 +112,33 @@ func TestLoadIngresses(t *testing.T) { }, }, } - endpoints := []k8s.Endpoints{ + endpoints := []*v1.Endpoints{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service1", UID: "1", Namespace: "testing", }, - Subsets: []k8s.EndpointSubset{ + Subsets: []v1.EndpointSubset{ { - Addresses: []k8s.EndpointAddress{ + Addresses: []v1.EndpointAddress{ { IP: "10.10.0.1", }, }, - Ports: []k8s.EndpointPort{ + Ports: []v1.EndpointPort{ { Port: 8080, }, }, }, { - Addresses: []k8s.EndpointAddress{ + Addresses: []v1.EndpointAddress{ { IP: "10.21.0.1", }, }, - Ports: []k8s.EndpointPort{ + Ports: []v1.EndpointPort{ { Port: 8080, }, @@ -142,19 +147,19 @@ func TestLoadIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service3", UID: "3", Namespace: "testing", }, - Subsets: []k8s.EndpointSubset{ + Subsets: []v1.EndpointSubset{ { - Addresses: []k8s.EndpointAddress{ + Addresses: []v1.EndpointAddress{ { IP: "10.15.0.1", }, }, - Ports: []k8s.EndpointPort{ + Ports: []v1.EndpointPort{ { Name: "http", Port: 8080, @@ -166,12 +171,12 @@ func TestLoadIngresses(t *testing.T) { }, }, { - Addresses: []k8s.EndpointAddress{ + Addresses: []v1.EndpointAddress{ { IP: "10.15.0.2", }, }, - Ports: []k8s.EndpointPort{ + Ports: []v1.EndpointPort{ { Name: "http", Port: 9080, @@ -267,23 +272,23 @@ func TestLoadIngresses(t *testing.T) { } func TestRuleType(t *testing.T) { - ingresses := []k8s.Ingress{ + ingresses := []*v1beta1.Ingress{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Annotations: map[string]string{"traefik.frontend.rule.type": "PathPrefixStrip"}, //camel case }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo1", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar1", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -294,21 +299,21 @@ func TestRuleType(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Annotations: map[string]string{"traefik.frontend.rule.type": "path"}, //lower case }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo1", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar2", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -319,21 +324,21 @@ func TestRuleType(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Annotations: map[string]string{"traefik.frontend.rule.type": "PathPrefix"}, //path prefix }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo2", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar1", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -344,21 +349,21 @@ func TestRuleType(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Annotations: map[string]string{"traefik.frontend.rule.type": "PathStrip"}, //path strip }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo2", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar2", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -369,21 +374,21 @@ func TestRuleType(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Annotations: map[string]string{"traefik.frontend.rule.type": "PathXXStrip"}, //wrong rule }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo1", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar3", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -394,15 +399,15 @@ func TestRuleType(t *testing.T) { }, }, } - services := []k8s.Service{ + services := []*v1.Service{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service1", UID: "1", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.1", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 801, @@ -495,22 +500,22 @@ func TestRuleType(t *testing.T) { } func TestGetPassHostHeader(t *testing.T) { - ingresses := []k8s.Ingress{{ - ObjectMeta: k8s.ObjectMeta{ + ingresses := []*v1beta1.Ingress{{ + ObjectMeta: v1.ObjectMeta{ Namespace: "awesome", }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -520,16 +525,16 @@ func TestGetPassHostHeader(t *testing.T) { }, }, }} - services := []k8s.Service{ + services := []*v1.Service{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service1", Namespace: "awesome", UID: "1", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.1", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 801, @@ -587,22 +592,22 @@ func TestGetPassHostHeader(t *testing.T) { } func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) { - ingresses := []k8s.Ingress{ + ingresses := []*v1beta1.Ingress{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "awesome", }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service", - ServicePort: k8s.FromInt(80), + ServicePort: intstr.FromInt(80), }, }, }, @@ -613,16 +618,16 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) { }, }, } - services := []k8s.Service{ + services := []*v1.Service{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service", UID: "1", Namespace: "awesome", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.1", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 80, @@ -631,14 +636,14 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service", UID: "2", Namespace: "not-awesome", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.2", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 80, @@ -693,23 +698,23 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) { } func TestLoadNamespacedIngresses(t *testing.T) { - ingresses := []k8s.Ingress{ + ingresses := []*v1beta1.Ingress{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "awesome", }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -718,19 +723,19 @@ func TestLoadNamespacedIngresses(t *testing.T) { }, { Host: "bar", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service3", - ServicePort: k8s.FromInt(443), + ServicePort: intstr.FromInt(443), }, }, { - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service2", - ServicePort: k8s.FromInt(802), + ServicePort: intstr.FromInt(802), }, }, }, @@ -741,21 +746,21 @@ func TestLoadNamespacedIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "not-awesome", }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "baz", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/baz", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -766,16 +771,16 @@ func TestLoadNamespacedIngresses(t *testing.T) { }, }, } - services := []k8s.Service{ + services := []*v1.Service{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "awesome", Name: "service1", UID: "1", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.1", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 801, @@ -784,14 +789,14 @@ func TestLoadNamespacedIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service1", Namespace: "not-awesome", UID: "1", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.1", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 801, @@ -800,14 +805,14 @@ func TestLoadNamespacedIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service2", Namespace: "awesome", UID: "2", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.2", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Port: 802, }, @@ -815,14 +820,14 @@ func TestLoadNamespacedIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service3", Namespace: "awesome", UID: "3", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.3", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 443, @@ -906,23 +911,23 @@ func TestLoadNamespacedIngresses(t *testing.T) { } func TestLoadMultipleNamespacedIngresses(t *testing.T) { - ingresses := []k8s.Ingress{ + ingresses := []*v1beta1.Ingress{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "awesome", }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "foo", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -931,19 +936,19 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, { Host: "bar", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service3", - ServicePort: k8s.FromInt(443), + ServicePort: intstr.FromInt(443), }, }, { - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service2", - ServicePort: k8s.FromInt(802), + ServicePort: intstr.FromInt(802), }, }, }, @@ -954,21 +959,21 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "somewhat-awesome", }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "awesome", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/quix", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -979,21 +984,21 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "not-awesome", }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { Host: "baz", - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/baz", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -1004,16 +1009,16 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, }, } - services := []k8s.Service{ + services := []*v1.Service{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service1", Namespace: "awesome", UID: "1", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.1", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 801, @@ -1022,14 +1027,14 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "somewhat-awesome", Name: "service1", UID: "17", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.4", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 801, @@ -1038,14 +1043,14 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "awesome", Name: "service2", UID: "2", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.2", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Port: 802, }, @@ -1053,14 +1058,14 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { }, }, { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Namespace: "awesome", Name: "service3", UID: "3", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.3", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 443, @@ -1167,21 +1172,21 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) { } func TestHostlessIngress(t *testing.T) { - ingresses := []k8s.Ingress{{ - ObjectMeta: k8s.ObjectMeta{ + ingresses := []*v1beta1.Ingress{{ + ObjectMeta: v1.ObjectMeta{ Namespace: "awesome", }, - Spec: k8s.IngressSpec{ - Rules: []k8s.IngressRule{ + Spec: v1beta1.IngressSpec{ + Rules: []v1beta1.IngressRule{ { - IngressRuleValue: k8s.IngressRuleValue{ - HTTP: &k8s.HTTPIngressRuleValue{ - Paths: []k8s.HTTPIngressPath{ + IngressRuleValue: v1beta1.IngressRuleValue{ + HTTP: &v1beta1.HTTPIngressRuleValue{ + Paths: []v1beta1.HTTPIngressPath{ { Path: "/bar", - Backend: k8s.IngressBackend{ + Backend: v1beta1.IngressBackend{ ServiceName: "service1", - ServicePort: k8s.FromInt(801), + ServicePort: intstr.FromInt(801), }, }, }, @@ -1191,16 +1196,16 @@ func TestHostlessIngress(t *testing.T) { }, }, }} - services := []k8s.Service{ + services := []*v1.Service{ { - ObjectMeta: k8s.ObjectMeta{ + ObjectMeta: v1.ObjectMeta{ Name: "service1", Namespace: "awesome", UID: "1", }, - Spec: k8s.ServiceSpec{ + Spec: v1.ServiceSpec{ ClusterIP: "10.0.0.1", - Ports: []k8s.ServicePort{ + Ports: []v1.ServicePort{ { Name: "http", Port: 801, @@ -1255,42 +1260,45 @@ func TestHostlessIngress(t *testing.T) { } type clientMock struct { - ingresses []k8s.Ingress - services []k8s.Service - endpoints []k8s.Endpoints + ingresses []*v1beta1.Ingress + services []*v1.Service + endpoints []*v1.Endpoints watchChan chan interface{} } -func (c clientMock) GetIngresses(labelString string, predicate func(k8s.Ingress) bool) ([]k8s.Ingress, error) { - var ingresses []k8s.Ingress +func (c clientMock) GetIngresses(namespaces k8s.Namespaces) []*v1beta1.Ingress { + result := make([]*v1beta1.Ingress, 0, len(c.ingresses)) + for _, ingress := range c.ingresses { - if predicate(ingress) { - ingresses = append(ingresses, ingress) + if k8s.HasNamespace(ingress, namespaces) { + result = append(result, ingress) } } - return ingresses, nil + return result } -func (c clientMock) WatchIngresses(labelString string, predicate func(k8s.Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error) { - return c.watchChan, make(chan error), nil + +func (c clientMock) WatchIngresses(labelSelector string, stopCh <-chan struct{}) chan interface{} { + return c.watchChan } -func (c clientMock) GetService(name, namespace string) (k8s.Service, error) { + +func (c clientMock) GetService(namespace, name string) (*v1.Service, bool, error) { for _, service := range c.services { if service.Namespace == namespace && service.Name == name { - return service, nil + return service, true, nil } } - return k8s.Service{}, nil + return &v1.Service{}, true, nil } -func (c clientMock) GetEndpoints(name, namespace string) (k8s.Endpoints, error) { +func (c clientMock) GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error) { for _, endpoints := range c.endpoints { if endpoints.Namespace == namespace && endpoints.Name == name { - return endpoints, nil + return endpoints, true, nil } } - return k8s.Endpoints{}, nil + return &v1.Endpoints{}, true, nil } -func (c clientMock) WatchAll(labelString string, stopCh <-chan bool) (chan interface{}, chan error, error) { - return c.watchChan, make(chan error), nil +func (c clientMock) WatchAll(labelString string, stopCh <-chan bool) (chan interface{}, error) { + return c.watchChan, nil } diff --git a/traefik.go b/traefik.go index 5cdef2497..142a232c3 100644 --- a/traefik.go +++ b/traefik.go @@ -19,7 +19,7 @@ import ( "github.com/containous/traefik/cluster" "github.com/containous/traefik/log" "github.com/containous/traefik/middlewares" - "github.com/containous/traefik/provider" + "github.com/containous/traefik/provider/k8s" "github.com/containous/traefik/types" "github.com/containous/traefik/version" "github.com/docker/libkv/store" @@ -144,7 +144,7 @@ Complete documentation is available at https://traefik.io`, f.AddParser(reflect.TypeOf(EntryPoints{}), &EntryPoints{}) f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{}) f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{}) - f.AddParser(reflect.TypeOf(provider.Namespaces{}), &provider.Namespaces{}) + f.AddParser(reflect.TypeOf(k8s.Namespaces{}), &k8s.Namespaces{}) f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{}) //add commands