Add kubernetes Ingress backend

Signed-off-by: Emile Vauge <emile@vauge.com>
This commit is contained in:
emile 2016-02-08 21:57:32 +01:00 committed by Emile Vauge
parent 6f47434833
commit 720912e880
No known key found for this signature in database
GPG key ID: D808B4C167352E59
18 changed files with 1191 additions and 201 deletions

6
cmd.go
View file

@ -51,6 +51,7 @@ var arguments = struct {
etcd bool etcd bool
etcdTLS bool etcdTLS bool
boltdb bool boltdb bool
kubernetes bool
}{ }{
GlobalConfiguration{ GlobalConfiguration{
EntryPoints: make(EntryPoints), EntryPoints: make(EntryPoints),
@ -73,6 +74,7 @@ var arguments = struct {
}, },
}, },
Boltdb: &provider.BoltDb{}, Boltdb: &provider.BoltDb{},
Kubernetes: &provider.Kubernetes{},
}, },
false, false,
false, false,
@ -86,6 +88,7 @@ var arguments = struct {
false, false,
false, false,
false, false,
false,
} }
func init() { func init() {
@ -167,6 +170,9 @@ func init() {
traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Endpoint, "boltdb.endpoint", "127.0.0.1:4001", "Boltdb server endpoint") traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Endpoint, "boltdb.endpoint", "127.0.0.1:4001", "Boltdb server endpoint")
traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Prefix, "boltdb.prefix", "/traefik", "Prefix used for KV store") traefikCmd.PersistentFlags().StringVar(&arguments.Boltdb.Prefix, "boltdb.prefix", "/traefik", "Prefix used for KV store")
traefikCmd.PersistentFlags().BoolVar(&arguments.kubernetes, "kubernetes", false, "Enable Kubernetes backend")
traefikCmd.PersistentFlags().StringVar(&arguments.Kubernetes.Endpoint, "kubernetes.endpoint", "127.0.0.1:8080", "Kubernetes server endpoint")
_ = viper.BindPFlag("configFile", traefikCmd.PersistentFlags().Lookup("configFile")) _ = viper.BindPFlag("configFile", traefikCmd.PersistentFlags().Lookup("configFile"))
_ = viper.BindPFlag("graceTimeOut", traefikCmd.PersistentFlags().Lookup("graceTimeOut")) _ = viper.BindPFlag("graceTimeOut", traefikCmd.PersistentFlags().Lookup("graceTimeOut"))
_ = viper.BindPFlag("logLevel", traefikCmd.PersistentFlags().Lookup("logLevel")) _ = viper.BindPFlag("logLevel", traefikCmd.PersistentFlags().Lookup("logLevel"))

View file

@ -37,6 +37,7 @@ type GlobalConfiguration struct {
Etcd *provider.Etcd Etcd *provider.Etcd
Zookeeper *provider.Zookepper Zookeeper *provider.Zookepper
Boltdb *provider.BoltDb Boltdb *provider.BoltDb
Kubernetes *provider.Kubernetes
} }
// DefaultEntryPoints holds default entry points // DefaultEntryPoints holds default entry points
@ -209,7 +210,11 @@ func LoadConfiguration() *GlobalConfiguration {
viper.AddConfigPath("$HOME/.traefik/") // call multiple times to add many search paths viper.AddConfigPath("$HOME/.traefik/") // call multiple times to add many search paths
viper.AddConfigPath(".") // optionally look for config in the working directory viper.AddConfigPath(".") // optionally look for config in the working directory
if err := viper.ReadInConfig(); err != nil { if err := viper.ReadInConfig(); err != nil {
fmtlog.Fatalf("Error reading file: %s", err) if len(viper.ConfigFileUsed()) > 0 {
fmtlog.Printf("Error reading configuration file: %s", err)
} else {
fmtlog.Printf("No configuration file found")
}
} }
if len(arguments.EntryPoints) > 0 { if len(arguments.EntryPoints) > 0 {
@ -254,6 +259,9 @@ func LoadConfiguration() *GlobalConfiguration {
if arguments.boltdb { if arguments.boltdb {
viper.Set("boltdb", arguments.Boltdb) viper.Set("boltdb", arguments.Boltdb)
} }
if arguments.kubernetes {
viper.Set("kubernetes", arguments.Kubernetes)
}
if err := unmarshal(&configuration); err != nil { if err := unmarshal(&configuration); err != nil {
fmtlog.Fatalf("Error reading file: %s", err) fmtlog.Fatalf("Error reading file: %s", err)

17
examples/compose-k8s.yaml Normal file
View file

@ -0,0 +1,17 @@
# etcd:
# image: gcr.io/google_containers/etcd:2.2.1
# net: host
# command: ['/usr/local/bin/etcd', '--addr=127.0.0.1:4001', '--bind-addr=0.0.0.0:4001', '--data-dir=/var/etcd/data']
kubelet:
image: gcr.io/google_containers/hyperkube-amd64:v1.2.2
privileged: true
pid: host
net : host
volumes:
- /:/rootfs:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:rw
- /var/lib/kubelet/:/var/lib/kubelet:rw
- /var/run:/var/run:rw
command: ['/hyperkube', 'kubelet', '--containerized', '--hostname-override=127.0.0.1', '--address=0.0.0.0', '--api-servers=http://localhost:8080', '--config=/etc/kubernetes/manifests', '--allow-privileged=true', '--v=2']

93
examples/k8s.ingress.yaml Normal file
View file

@ -0,0 +1,93 @@
# 3 Services for the 3 endpoints of the Ingress
apiVersion: v1
kind: Service
metadata:
name: whoami-x
labels:
app: whoami
spec:
type: NodePort
ports:
- port: 80
nodePort: 30301
targetPort: 80
protocol: TCP
name: http
selector:
app: whoami
---
apiVersion: v1
kind: Service
metadata:
name: whoami-default
labels:
app: whoami
spec:
type: NodePort
ports:
- port: 80
nodePort: 30302
targetPort: 80
protocol: TCP
name: http
selector:
app: whoami
---
apiVersion: v1
kind: Service
metadata:
name: whoami-y
labels:
app: whoami
spec:
type: NodePort
ports:
- port: 80
nodePort: 30284
targetPort: 80
protocol: TCP
name: http
selector:
app: whoami
---
# A single RC matching all Services
apiVersion: v1
kind: ReplicationController
metadata:
name: whoami
spec:
replicas: 1
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: emilevauge/whoami
ports:
- containerPort: 80
---
# An Ingress with 2 hosts and 3 endpoints
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: whoamimap
spec:
rules:
- host: foo.localhost
http:
paths:
- path: /bar
backend:
serviceName: whoami-x
servicePort: 80
- host: bar.localhost
http:
paths:
- backend:
serviceName: whoami-y
servicePort: 80
- backend:
serviceName: whoami-x
servicePort: 80

10
examples/k8s.namespace.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/bash
kubectl create -f - << EOF
kind: Namespace
apiVersion: v1
metadata:
name: kube-system
labels:
name: kube-system
EOF

36
examples/k8s.rc.yaml Normal file
View file

@ -0,0 +1,36 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: traefik-ingress-controller
labels:
k8s-app: traefik-ingress-lb
spec:
replicas: 1
selector:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
terminationGracePeriodSeconds: 60
containers:
- image: containous/traefik:k8s
name: traefik-ingress-lb
imagePullPolicy: Always
# livenessProbe:
# httpGet:
# path: /healthz
# port: 10249
# scheme: HTTP
# initialDelaySeconds: 30
# timeoutSeconds: 5
ports:
- containerPort: 80
hostPort: 80
- containerPort: 443
hostPort: 443
args:
- --kubernetes
- --logLevel=DEBUG

28
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: fffa87220825895f7e3a6ceed3b13ecbf6bc934ab072fc9be3d00e3eef411ecb hash: 6fe539ee86a9dc90a67b60f42b027c72359bed0ca22e7a94355ad80f37a32d68
updated: 2016-04-13T14:05:41.300658168+02:00 updated: 2016-04-18T21:31:13.195184921+02:00
imports: imports:
- name: github.com/alecthomas/template - name: github.com/alecthomas/template
version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0 version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0
@ -8,7 +8,7 @@ imports:
- name: github.com/boltdb/bolt - name: github.com/boltdb/bolt
version: 51f99c862475898df9773747d3accd05a7ca33c1 version: 51f99c862475898df9773747d3accd05a7ca33c1
- name: github.com/BurntSushi/toml - name: github.com/BurntSushi/toml
version: bd2bdf7f18f849530ef7a1c29a4290217cab32a1 version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f
- name: github.com/BurntSushi/ty - name: github.com/BurntSushi/ty
version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74 version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74
subpackages: subpackages:
@ -29,6 +29,7 @@ imports:
- memmetrics - memmetrics
- roundrobin - roundrobin
- utils - utils
- connlimit
- stream - stream
- name: github.com/coreos/go-etcd - name: github.com/coreos/go-etcd
version: cc90c7b091275e606ad0ca7102a23fb2072f3f5e version: cc90c7b091275e606ad0ca7102a23fb2072f3f5e
@ -42,7 +43,6 @@ imports:
version: ff6f38ccb69afa96214c7ee955359465d1fc767a version: ff6f38ccb69afa96214c7ee955359465d1fc767a
subpackages: subpackages:
- reference - reference
- digest
- name: github.com/docker/docker - name: github.com/docker/docker
version: f39987afe8d611407887b3094c03d6ba6a766a67 version: f39987afe8d611407887b3094c03d6ba6a766a67
subpackages: subpackages:
@ -94,13 +94,11 @@ imports:
- client/transport - client/transport
- client/transport/cancellable - client/transport/cancellable
- types/network - types/network
- types/reference
- types/registry - types/registry
- types/time - types/time
- types/versions
- types/blkiodev - types/blkiodev
- name: github.com/docker/go-connections - name: github.com/docker/go-connections
version: f549a9393d05688dff0992ef3efd8bbe6c628aeb version: 5b7154ba2efe13ff86ae8830a9e7cb120b080d6e
subpackages: subpackages:
- nat - nat
- sockets - sockets
@ -148,7 +146,7 @@ imports:
subpackages: subpackages:
- api - api
- name: github.com/hashicorp/hcl - name: github.com/hashicorp/hcl
version: 2604f3bda7e8960c1be1063709e7d7f0765048d0 version: 27a57f2605e04995c111273c263d51cee60d9bc4
subpackages: subpackages:
- hcl/ast - hcl/ast
- hcl/parser - hcl/parser
@ -177,13 +175,17 @@ imports:
- name: github.com/Microsoft/go-winio - name: github.com/Microsoft/go-winio
version: 862b6557927a5c5c81e411c12aa6de7e566cbb7a version: 862b6557927a5c5c81e411c12aa6de7e566cbb7a
- name: github.com/miekg/dns - name: github.com/miekg/dns
version: dd83d5cbcfd986f334b2747feeb907e281318fdf version: a5cc44dc6b2eee8eddfd6581e1c6bb753ff0d176
- name: github.com/mitchellh/mapstructure - name: github.com/mitchellh/mapstructure
version: d2dd0262208475919e1a362f675cfc0e7c10e905 version: d2dd0262208475919e1a362f675cfc0e7c10e905
- name: github.com/moul/http2curl
version: 1812aee76a1ce98d604a44200c6a23c689b17a89
- name: github.com/opencontainers/runc - name: github.com/opencontainers/runc
version: 4ab132458fc3e9dbeea624153e0331952dc4c8d5 version: 4ab132458fc3e9dbeea624153e0331952dc4c8d5
subpackages: subpackages:
- libcontainer/user - libcontainer/user
- name: github.com/parnurzeal/gorequest
version: 91b42fce877cc6af96c45818665a4c615cc5f4ee
- name: github.com/pmezard/go-difflib - name: github.com/pmezard/go-difflib
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
subpackages: subpackages:
@ -203,7 +205,7 @@ imports:
- name: github.com/spf13/jwalterweatherman - name: github.com/spf13/jwalterweatherman
version: 33c24e77fb80341fe7130ee7c594256ff08ccc46 version: 33c24e77fb80341fe7130ee7c594256ff08ccc46
- name: github.com/spf13/pflag - name: github.com/spf13/pflag
version: 1f296710f879815ad9e6d39d947c828c3e4b4c3d version: 8f6a28b0916586e7f22fe931ae2fcfc380b1c0e6
- name: github.com/spf13/viper - name: github.com/spf13/viper
version: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325 version: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325
- name: github.com/streamrail/concurrent-map - name: github.com/streamrail/concurrent-map
@ -220,7 +222,7 @@ imports:
- name: github.com/unrolled/render - name: github.com/unrolled/render
version: 26b4e3aac686940fe29521545afad9966ddfc80c version: 26b4e3aac686940fe29521545afad9966ddfc80c
- name: github.com/vdemeester/docker-events - name: github.com/vdemeester/docker-events
version: 6ea3f28df37f29a47498bc8b32b36ad8491dbd37 version: 1ecaca5890ef1ffd266fcbfdbe43073ef105704b
- name: github.com/vdemeester/libkermit - name: github.com/vdemeester/libkermit
version: 7e4e689a6fa9281e0fb9b7b9c297e22d5342a5ec version: 7e4e689a6fa9281e0fb9b7b9c297e22d5342a5ec
- name: github.com/vdemeester/shakers - name: github.com/vdemeester/shakers
@ -243,11 +245,11 @@ imports:
- name: github.com/wendal/errors - name: github.com/wendal/errors
version: f66c77a7882b399795a8987ebf87ef64a427417e version: f66c77a7882b399795a8987ebf87ef64a427417e
- name: github.com/xenolf/lego - name: github.com/xenolf/lego
version: 23e88185c255e95a106835d80e76e5a3a66d7c54 version: 684400fe76a813e78d87803a62bc04d977c501d2
subpackages: subpackages:
- acme - acme
- name: golang.org/x/crypto - name: golang.org/x/crypto
version: d68c3ecb62c850b645dc072a8d78006286bf81ca version: 1777f3ba8c1fed80fcaec3317e3aaa4f627764d2
subpackages: subpackages:
- ocsp - ocsp
- name: golang.org/x/net - name: golang.org/x/net

View file

@ -1,65 +1,65 @@
package: main package: main
import: import:
- package: github.com/coreos/go-etcd - package: github.com/coreos/go-etcd
ref: cc90c7b091275e606ad0ca7102a23fb2072f3f5e version: cc90c7b091275e606ad0ca7102a23fb2072f3f5e
subpackages: subpackages:
- etcd - etcd
- package: github.com/mailgun/log - package: github.com/mailgun/log
ref: 44874009257d4d47ba9806f1b7f72a32a015e4d8 version: 44874009257d4d47ba9806f1b7f72a32a015e4d8
- package: github.com/containous/oxy - package: github.com/containous/oxy
ref: 021f82bd8260ba15f5862a9fe62018437720dff5 version: 021f82bd8260ba15f5862a9fe62018437720dff5
subpackages: subpackages:
- cbreaker - cbreaker
- forward - forward
- memmetrics - memmetrics
- roundrobin - roundrobin
- utils - utils
- package: github.com/hashicorp/consul - package: github.com/hashicorp/consul
ref: de080672fee9e6104572eeea89eccdca135bb918 version: de080672fee9e6104572eeea89eccdca135bb918
subpackages: subpackages:
- api - api
- package: github.com/samuel/go-zookeeper - package: github.com/samuel/go-zookeeper
ref: fa6674abf3f4580b946a01bf7a1ce4ba8766205b version: fa6674abf3f4580b946a01bf7a1ce4ba8766205b
subpackages: subpackages:
- zk - zk
- package: github.com/docker/libtrust - package: github.com/docker/libtrust
ref: 9cbd2a1374f46905c68a4eb3694a130610adc62a version: 9cbd2a1374f46905c68a4eb3694a130610adc62a
- package: github.com/go-check/check - package: github.com/go-check/check
ref: 11d3bc7aa68e238947792f30573146a3231fc0f1 version: 11d3bc7aa68e238947792f30573146a3231fc0f1
- package: golang.org/x/net - package: golang.org/x/net
ref: d9558e5c97f85372afee28cf2b6059d7d3818919 version: d9558e5c97f85372afee28cf2b6059d7d3818919
subpackages: subpackages:
- context - context
- package: github.com/gorilla/handlers - package: github.com/gorilla/handlers
ref: 40694b40f4a928c062f56849989d3e9cd0570e5f version: 40694b40f4a928c062f56849989d3e9cd0570e5f
- package: github.com/docker/libkv - package: github.com/docker/libkv
ref: 3732f7ff1b56057c3158f10bceb1e79133025373 version: 3732f7ff1b56057c3158f10bceb1e79133025373
- package: github.com/alecthomas/template - package: github.com/alecthomas/template
ref: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0 version: b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0
- package: github.com/vdemeester/shakers - package: github.com/vdemeester/shakers
ref: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 version: 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
- package: github.com/alecthomas/units - package: github.com/alecthomas/units
ref: 6b4e7dc5e3143b85ea77909c72caf89416fc2915 version: 6b4e7dc5e3143b85ea77909c72caf89416fc2915
- package: github.com/gambol99/go-marathon - package: github.com/gambol99/go-marathon
ref: ade11d1dc2884ee1f387078fc28509559b6235d1 version: ade11d1dc2884ee1f387078fc28509559b6235d1
- package: github.com/vulcand/predicate - package: github.com/vulcand/predicate
ref: cb0bff91a7ab7cf7571e661ff883fc997bc554a3 version: cb0bff91a7ab7cf7571e661ff883fc997bc554a3
- package: github.com/thoas/stats - package: github.com/thoas/stats
ref: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8 version: 54ed61c2b47e263ae2f01b86837b0c4bd1da28e8
- package: github.com/Sirupsen/logrus - package: github.com/Sirupsen/logrus
ref: 418b41d23a1bf978c06faea5313ba194650ac088 version: 418b41d23a1bf978c06faea5313ba194650ac088
- package: github.com/unrolled/render - package: github.com/unrolled/render
ref: 26b4e3aac686940fe29521545afad9966ddfc80c version: 26b4e3aac686940fe29521545afad9966ddfc80c
- package: github.com/flynn/go-shlex - package: github.com/flynn/go-shlex
ref: 3f9db97f856818214da2e1057f8ad84803971cff version: 3f9db97f856818214da2e1057f8ad84803971cff
- package: github.com/boltdb/bolt - package: github.com/boltdb/bolt
ref: 51f99c862475898df9773747d3accd05a7ca33c1 version: 51f99c862475898df9773747d3accd05a7ca33c1
- package: gopkg.in/mgo.v2 - package: gopkg.in/mgo.v2
ref: 22287bab4379e1fbf6002fb4eb769888f3fb224c version: 22287bab4379e1fbf6002fb4eb769888f3fb224c
subpackages: subpackages:
- bson - bson
- package: github.com/docker/docker - package: github.com/docker/docker
ref: f39987afe8d611407887b3094c03d6ba6a766a67 version: f39987afe8d611407887b3094c03d6ba6a766a67
subpackages: subpackages:
- autogen - autogen
- api - api
@ -97,81 +97,89 @@ import:
- runconfig - runconfig
- utils - utils
- volume - volume
- package: github.com/mailgun/timetools - package: github.com/mailgun/timetools
ref: fd192d755b00c968d312d23f521eb0cdc6f66bd0 version: fd192d755b00c968d312d23f521eb0cdc6f66bd0
- package: github.com/codegangsta/negroni - package: github.com/codegangsta/negroni
ref: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b
- package: gopkg.in/yaml.v2 - package: gopkg.in/yaml.v2
ref: 7ad95dd0798a40da1ccdff6dff35fd177b5edf40 version: 7ad95dd0798a40da1ccdff6dff35fd177b5edf
- package: github.com/opencontainers/runc - package: github.com/opencontainers/runc
ref: 4ab132458fc3e9dbeea624153e0331952dc4c8d5 version: 4ab132458fc3e9dbeea624153e0331952dc4c8d5
subpackages: subpackages:
- libcontainer/user - libcontainer/user
- package: github.com/gorilla/mux - package: github.com/gorilla/mux
ref: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a version: f15e0c49460fd49eebe2bcc8486b05d1bef68d3a
- package: github.com/BurntSushi/ty - package: github.com/BurntSushi/ty
ref: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74 version: 6add9cd6ad42d389d6ead1dde60b4ad71e46fd74
- package: github.com/elazarl/go-bindata-assetfs - package: github.com/elazarl/go-bindata-assetfs
ref: d5cac425555ca5cf00694df246e04f05e6a55150 version: d5cac425555ca5cf00694df246e04f05e6a55150
- package: github.com/BurntSushi/toml - package: github.com/BurntSushi/toml
ref: bd2bdf7f18f849530ef7a1c29a4290217cab32a1 version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f
- package: gopkg.in/alecthomas/kingpin.v2 - package: gopkg.in/alecthomas/kingpin.v2
ref: 639879d6110b1b0409410c7b737ef0bb18325038 version: 639879d6110b1b0409410c7b737ef0bb18325038
- package: github.com/cenkalti/backoff - package: github.com/cenkalti/backoff
ref: 4dc77674aceaabba2c7e3da25d4c823edfb73f99 version: 4dc77674aceaabba2c7e3da25d4c823edfb73f99
- package: gopkg.in/fsnotify.v1 - package: gopkg.in/fsnotify.v1
ref: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0 version: 96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0
- package: github.com/mailgun/manners - package: github.com/mailgun/manners
ref: fada45142db3f93097ca917da107aa3fad0ffcb5 version: fada45142db3f93097ca917da107aa3fad0ffcb5
- package: github.com/gorilla/context - package: github.com/gorilla/context
ref: 215affda49addc4c8ef7e2534915df2c8c35c6cd version: 215affda49addc4c8ef7e2534915df2c8c35c6cd
- package: github.com/codahale/hdrhistogram - package: github.com/codahale/hdrhistogram
ref: 954f16e8b9ef0e5d5189456aa4c1202758e04f17 version: 954f16e8b9ef0e5d5189456aa4c1202758e04f17
- package: github.com/gorilla/websocket - package: github.com/gorilla/websocket
- package: github.com/donovanhide/eventsource - package: github.com/donovanhide/eventsource
ref: d8a3071799b98cacd30b6da92f536050ccfe6da4 version: d8a3071799b98cacd30b6da92f536050ccfe6da4
- package: github.com/golang/glog - package: github.com/golang/glog
ref: fca8c8854093a154ff1eb580aae10276ad6b1b5f version: fca8c8854093a154ff1eb580aae10276ad6b1b5f
- package: github.com/spf13/cast - package: github.com/spf13/cast
ref: ee7b3e0353166ab1f3a605294ac8cd2b77953778 version: ee7b3e0353166ab1f3a605294ac8cd2b77953778
- package: github.com/mitchellh/mapstructure - package: github.com/mitchellh/mapstructure
- package: github.com/spf13/jwalterweatherman - package: github.com/spf13/jwalterweatherman
- package: github.com/spf13/pflag - package: github.com/spf13/pflag
- package: github.com/wendal/errors - package: github.com/wendal/errors
- package: github.com/hashicorp/hcl - package: github.com/hashicorp/hcl
- package: github.com/kr/pretty - package: github.com/kr/pretty
- package: github.com/magiconair/properties - package: github.com/magiconair/properties
- package: github.com/kr/text - package: github.com/kr/text
- package: github.com/spf13/viper - package: github.com/spf13/viper
ref: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325 version: a212099cbe6fbe8d07476bfda8d2d39b6ff8f325
- package: github.com/spf13/cobra - package: github.com/spf13/cobra
subpackages: subpackages:
- /cobra - cobra
- package: github.com/google/go-querystring/query - package: github.com/google/go-querystring
- package: github.com/vulcand/vulcand/plugin/rewrite subpackages:
- package: github.com/stretchr/testify/mock - query
- package: github.com/xenolf/lego - package: github.com/vulcand/vulcand
- package: github.com/vdemeester/libkermit subpackages:
ref: 7e4e689a6fa9281e0fb9b7b9c297e22d5342a5ec - plugin/rewrite
- package: github.com/docker/libcompose - package: github.com/stretchr/testify
subpackages:
- mock
- package: github.com/xenolf/lego
- package: github.com/vdemeester/libkermit
version: 7e4e689a6fa9281e0fb9b7b9c297e22d5342a5ec
- package: github.com/docker/libcompose
version: e290a513ba909ca3afefd5cd611f3a3fe56f6a3a version: e290a513ba909ca3afefd5cd611f3a3fe56f6a3a
- package: github.com/docker/distribution - package: github.com/docker/distribution
version: ff6f38ccb69afa96214c7ee955359465d1fc767a version: ff6f38ccb69afa96214c7ee955359465d1fc767a
subpackages: subpackages:
- reference - reference
- package: github.com/docker/engine-api - package: github.com/docker/engine-api
version: 8924d6900370b4c7e7984be5adc61f50a80d7537
subpackages: subpackages:
- client - client
- types - types
- types/container - types/container
- types/filters - types/filters
- types/strslice - types/strslice
- package: github.com/vdemeester/docker-events - package: github.com/vdemeester/docker-events
- package: github.com/docker/go-connections - package: github.com/docker/go-connections
subpackages: subpackages:
- nat - nat
- sockets - sockets
- tlsconfig - tlsconfig
- package: github.com/docker/go-units - package: github.com/docker/go-units
- package: github.com/mailgun/multibuf - package: github.com/mailgun/multibuf
- package: github.com/streamrail/concurrent-map - package: github.com/streamrail/concurrent-map
- package: github.com/parnurzeal/gorequest

View file

@ -19,14 +19,14 @@ func (s *SimpleSuite) TestNoOrInexistentConfigShouldFail(c *check.C) {
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
c.Assert(err, checker.NotNil) c.Assert(err, checker.NotNil)
c.Assert(string(output), checker.Contains, "Error reading file: open : no such file or directory") c.Assert(string(output), checker.Contains, "No configuration file found")
nonExistentFile := "non/existent/file.toml" nonExistentFile := "non/existent/file.toml"
cmd = exec.Command(traefikBinary, "--configFile="+nonExistentFile) cmd = exec.Command(traefikBinary, "--configFile="+nonExistentFile)
output, err = cmd.CombinedOutput() output, err = cmd.CombinedOutput()
c.Assert(err, checker.NotNil) c.Assert(err, checker.NotNil)
c.Assert(string(output), checker.Contains, fmt.Sprintf("Error reading file: open %s: no such file or directory", nonExistentFile)) c.Assert(string(output), checker.Contains, fmt.Sprintf("Error reading configuration file: open %s: no such file or directory", nonExistentFile))
} }
func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) { func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) {
@ -34,7 +34,7 @@ func (s *SimpleSuite) TestInvalidConfigShouldFail(c *check.C) {
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
c.Assert(err, checker.NotNil) c.Assert(err, checker.NotNil)
c.Assert(string(output), checker.Contains, "Error reading file: While parsing config: Near line 1") c.Assert(string(output), checker.Contains, "While parsing config: Near line 0 (last key parsed ''): Bare keys cannot contain '{'")
} }
func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) { func (s *SimpleSuite) TestSimpleDefaultConfig(c *check.C) {

View file

@ -743,11 +743,11 @@ func TestDockerLoadDockerConfig(t *testing.T) {
}, },
}, },
expectedFrontends: map[string]*types.Frontend{ expectedFrontends: map[string]*types.Frontend{
`"frontend-Host-test-docker-localhost"`: { "frontend-Host-test-docker-localhost": {
Backend: "backend-test", Backend: "backend-test",
EntryPoints: []string{}, EntryPoints: []string{},
Routes: map[string]types.Route{ Routes: map[string]types.Route{
`"route-frontend-Host-test-docker-localhost"`: { "route-frontend-Host-test-docker-localhost": {
Rule: "Host:test.docker.localhost", Rule: "Host:test.docker.localhost",
}, },
}, },
@ -815,20 +815,20 @@ func TestDockerLoadDockerConfig(t *testing.T) {
}, },
}, },
expectedFrontends: map[string]*types.Frontend{ expectedFrontends: map[string]*types.Frontend{
`"frontend-Host-test1-docker-localhost"`: { "frontend-Host-test1-docker-localhost": {
Backend: "backend-foobar", Backend: "backend-foobar",
EntryPoints: []string{"http", "https"}, EntryPoints: []string{"http", "https"},
Routes: map[string]types.Route{ Routes: map[string]types.Route{
`"route-frontend-Host-test1-docker-localhost"`: { "route-frontend-Host-test1-docker-localhost": {
Rule: "Host:test1.docker.localhost", Rule: "Host:test1.docker.localhost",
}, },
}, },
}, },
`"frontend-Host-test2-docker-localhost"`: { "frontend-Host-test2-docker-localhost": {
Backend: "backend-foobar", Backend: "backend-foobar",
EntryPoints: []string{}, EntryPoints: []string{},
Routes: map[string]types.Route{ Routes: map[string]types.Route{
`"route-frontend-Host-test2-docker-localhost"`: { "route-frontend-Host-test2-docker-localhost": {
Rule: "Host:test2.docker.localhost", Rule: "Host:test2.docker.localhost",
}, },
}, },

163
provider/k8s/client.go Normal file
View file

@ -0,0 +1,163 @@
package k8s
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"github.com/containous/traefik/safe"
"github.com/parnurzeal/gorequest"
"net/http"
"net/url"
"strings"
)
const (
// APIEndpoint defines the base path for kubernetes API resources.
APIEndpoint = "/api/v1"
defaultService = "/namespaces/default/services"
extentionsEndpoint = "/apis/extensions/v1beta1"
defaultIngress = "/ingresses"
)
// Client is a client for the Kubernetes master.
type Client struct {
endpointURL string
tls *tls.Config
token string
caCert []byte
}
// 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)
if err != nil {
return nil, fmt.Errorf("failed to parse URL %q: %v", baseURL, err)
}
return &Client{
endpointURL: strings.TrimSuffix(validURL.String(), "/"),
token: token,
caCert: caCert,
}, nil
}
// GetIngresses returns all services in the cluster
func (c *Client) GetIngresses(predicate func(Ingress) bool) ([]Ingress, error) {
getURL := c.endpointURL + extentionsEndpoint + defaultIngress
request := gorequest.New().Get(getURL)
if len(c.token) > 0 {
request.Header["Authorization"] = "Bearer " + c.token
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(c.caCert)
c.tls = &tls.Config{RootCAs: pool}
}
res, body, errs := request.TLSClientConfig(c.tls).EndBytes()
if errs != nil {
return nil, fmt.Errorf("failed to create request: GET %q : %v", getURL, errs)
}
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("http error %d GET %q: %q", res.StatusCode, getURL, string(body))
}
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)
}
}
return ingresses, nil
}
// WatchIngresses returns all services in the cluster
func (c *Client) WatchIngresses(predicate func(Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error) {
watchCh := make(chan interface{})
errCh := make(chan error)
getURL := c.endpointURL + extentionsEndpoint + defaultIngress + "?watch=true"
// Make request to Kubernetes API
request := gorequest.New().Get(getURL)
if len(c.token) > 0 {
request.Set("Authorization", "Bearer "+c.token)
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(c.caCert)
c.tls = &tls.Config{RootCAs: pool}
}
req, err := request.TLSClientConfig(c.tls).MakeRequest()
if err != nil {
return watchCh, errCh, fmt.Errorf("failed to create request: GET %q : %v", getURL, err)
}
request.Client.Transport = request.Transport
res, err := request.Client.Do(req)
if err != nil {
return watchCh, errCh, fmt.Errorf("failed to make request: GET %q: %v", getURL, err)
}
shouldStop := safe.New(false)
go func() {
select {
case <-stopCh:
shouldStop.Set(true)
res.Body.Close()
return
}
}()
go func() {
defer close(watchCh)
defer close(errCh)
for {
var ingressList interface{}
if err := json.NewDecoder(res.Body).Decode(&ingressList); err != nil {
if !shouldStop.Get().(bool) {
errCh <- fmt.Errorf("failed to decode list of ingress resources: %v", err)
}
return
}
watchCh <- ingressList
}
}()
return watchCh, errCh, nil
}
// GetServices returns all services in the cluster
func (c *Client) GetServices(predicate func(Service) bool) ([]Service, error) {
getURL := c.endpointURL + APIEndpoint + defaultService
// Make request to Kubernetes API
request := gorequest.New().Get(getURL)
if len(c.token) > 0 {
request.Header["Authorization"] = "Bearer " + c.token
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(c.caCert)
c.tls = &tls.Config{RootCAs: pool}
}
res, body, errs := request.TLSClientConfig(c.tls).EndBytes()
if errs != nil {
return nil, fmt.Errorf("failed to create request: GET %q : %v", getURL, errs)
}
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("http error %d GET %q: %q", res.StatusCode, getURL, string(body))
}
var serviceList ServiceList
if err := json.Unmarshal(body, &serviceList); err != nil {
return nil, fmt.Errorf("failed to decode list of services resources: %v", err)
}
services := serviceList.Items[:0]
for _, service := range serviceList.Items {
if predicate(service) {
services = append(services, service)
}
}
return services, nil
}

151
provider/k8s/ingress.go Normal file
View file

@ -0,0 +1,151 @@
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://<host>/<path>?<searchpart> -> 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"`
}

313
provider/k8s/service.go Normal file
View file

@ -0,0 +1,313 @@
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
}
// 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"`
}

164
provider/kubernetes.go Normal file
View file

@ -0,0 +1,164 @@
package provider
import (
log "github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
"github.com/containous/traefik/provider/k8s"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"io/ioutil"
"os"
"text/template"
"time"
)
const (
serviceAccountToken = "/var/run/secrets/kubernetes.io/serviceaccount/token"
serviceAccountCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
)
// Kubernetes holds configurations of the Kubernetes provider.
type Kubernetes struct {
BaseProvider `mapstructure:",squash"`
Endpoint string
}
// 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) error {
var token string
tokenBytes, err := ioutil.ReadFile(serviceAccountToken)
if err == nil {
token = string(tokenBytes)
log.Debugf("Kubernetes token: %s", token)
} else {
log.Debugf("Kubernetes load token error: %s", err)
}
caCert, err := ioutil.ReadFile(serviceAccountCACert)
if err == nil {
log.Debugf("Kubernetes CA cert: %s", serviceAccountCACert)
} else {
log.Debugf("Kubernetes load token error: %s", err)
}
kubernetesHost := os.Getenv("KUBERNETES_SERVICE_HOST")
kubernetesPort := os.Getenv("KUBERNETES_SERVICE_PORT_HTTPS")
if len(kubernetesPort) > 0 && len(kubernetesHost) > 0 {
provider.Endpoint = "https://" + kubernetesHost + ":" + kubernetesPort
}
log.Debugf("Kubernetes endpoint: %s", provider.Endpoint)
k8sClient, err := k8s.NewClient(provider.Endpoint, caCert, token)
if err != nil {
return err
}
pool.Go(func(stop chan bool) {
stopWatch := make(chan bool)
operation := func() error {
select {
case <-stop:
return nil
default:
}
ingressesChan, errChan, err := k8sClient.WatchIngresses(func(ingress k8s.Ingress) bool {
return true
}, stopWatch)
if err != nil {
log.Errorf("Error retrieving ingresses: %v", err)
return err
}
for {
templateObjects := types.Configuration{
map[string]*types.Backend{},
map[string]*types.Frontend{},
}
select {
case <-stop:
stopWatch <- true
return nil
case err := <-errChan:
return err
case event := <-ingressesChan:
log.Debugf("Received event from kubenetes %+v", event)
ingresses, err := k8sClient.GetIngresses(func(ingress k8s.Ingress) bool {
return true
})
if err != nil {
log.Errorf("Error retrieving ingresses: %+v", err)
continue
}
for _, i := range ingresses {
for _, r := range i.Spec.Rules {
for _, pa := range r.HTTP.Paths {
if _, exists := templateObjects.Backends[r.Host+pa.Path]; !exists {
templateObjects.Backends[r.Host+pa.Path] = &types.Backend{
Servers: make(map[string]types.Server),
}
}
if _, exists := templateObjects.Frontends[r.Host+pa.Path]; !exists {
templateObjects.Frontends[r.Host+pa.Path] = &types.Frontend{
Backend: r.Host + pa.Path,
Routes: make(map[string]types.Route),
}
}
if _, exists := templateObjects.Frontends[r.Host+pa.Path].Routes[r.Host]; !exists {
templateObjects.Frontends[r.Host+pa.Path].Routes[r.Host] = types.Route{
Rule: "Host:" + r.Host,
}
}
if len(pa.Path) > 0 {
templateObjects.Frontends[r.Host+pa.Path].Routes[pa.Path] = types.Route{
Rule: "Path:" + pa.Path,
}
}
services, err := k8sClient.GetServices(func(service k8s.Service) bool {
return service.Name == pa.Backend.ServiceName
})
if err != nil {
log.Errorf("Error retrieving services: %v", err)
continue
}
for _, service := range services {
var protocol string
for _, port := range service.Spec.Ports {
if port.Port == pa.Backend.ServicePort.IntValue() {
protocol = port.Name
break
}
}
templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{
URL: protocol + "://" + service.Spec.ClusterIP + ":" + pa.Backend.ServicePort.String(),
Weight: 1,
}
}
}
}
}
configurationChan <- types.ConfigMessage{
ProviderName: "kubernetes",
Configuration: provider.loadConfig(templateObjects),
}
}
}
}
notify := func(err error, time time.Duration) {
log.Errorf("Kubernetes connection error %+v, retrying in %s", err, time)
}
err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
if err != nil {
log.Fatalf("Cannot connect to Kubernetes server %+v", err)
}
})
return nil
}
func (provider *Kubernetes) loadConfig(templateObjects types.Configuration) *types.Configuration {
var FuncMap = template.FuncMap{}
configuration, err := provider.getConfiguration("templates/kubernetes.tmpl", FuncMap, templateObjects)
if err != nil {
log.Error(err)
}
return configuration
}

View file

@ -74,7 +74,7 @@ func TestConfigurationErrors(t *testing.T) {
Filename: templateInvalidTOMLFile.Name(), Filename: templateInvalidTOMLFile.Name(),
}, },
}, },
expectedError: "Near line 1, key 'Hello': Near line 1: Expected key separator '=', but got '<' instead", expectedError: "Near line 1 (last key parsed 'Hello'): Expected key separator '=', but got '<' instead",
funcMap: template.FuncMap{ funcMap: template.FuncMap{
"Foo": func() string { "Foo": func() string {
return "bar" return "bar"

View file

@ -22,4 +22,4 @@ if [ -z "$DATE" ]; then
fi fi
# Build binaries # Build binaries
CGO_ENABLED=0 GOGC=off go build $FLAGS -ldflags "-X main.Version=$VERSION -X main.BuildDate=$DATE" -a -installsuffix nocgo -o dist/traefik . CGO_ENABLED=0 GOGC=off go build $FLAGS -ldflags "-s -w -X main.Version=$VERSION -X main.BuildDate=$DATE" -a -installsuffix nocgo -o dist/traefik .

View file

@ -236,6 +236,9 @@ func (server *Server) configureProviders() {
if server.globalConfiguration.Boltdb != nil { if server.globalConfiguration.Boltdb != nil {
server.providers = append(server.providers, server.globalConfiguration.Boltdb) server.providers = append(server.providers, server.globalConfiguration.Boltdb)
} }
if server.globalConfiguration.Kubernetes != nil {
server.providers = append(server.providers, server.globalConfiguration.Kubernetes)
}
} }
func (server *Server) startProviders() { func (server *Server) startProviders() {

16
templates/kubernetes.tmpl Normal file
View file

@ -0,0 +1,16 @@
[backends]{{range $backendName, $backend := .Backends}}
{{range $serverName, $server := $backend.Servers}}
[backends."{{$backendName}}".servers."{{$serverName}}"]
url = "{{$server.URL}}"
weight = {{$server.Weight}}
{{end}}
{{end}}
[frontends]{{range $frontendName, $frontend := .Frontends}}
[frontends."{{$frontendName}}"]
backend = "{{$frontend.Backend}}"
{{range $routeName, $route := $frontend.Routes}}
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
rule = "{{$route.Rule}}"
{{end}}
{{end}}