Custom resource definition
Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
This commit is contained in:
parent
cfaf47c8a2
commit
4c060a78cc
1348 changed files with 92364 additions and 55766 deletions
232
Gopkg.lock
generated
232
Gopkg.lock
generated
|
@ -135,21 +135,6 @@
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "a3fa4a771d87bda2514a90a157e1fed1b6897d2e"
|
revision = "a3fa4a771d87bda2514a90a157e1fed1b6897d2e"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:975b8fd9f8bf97cfdbc6a6af823172e4315b8e0b1d99f7651472f4fe9ff61cbe"
|
|
||||||
name = "github.com/PuerkitoBio/purell"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "8a290539e2e8629dbc4e6bad948158f790ec31f4"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:8098cd40cd09879efbf12e33bcd51ead4a66006ac802cd563a66c4f3373b9727"
|
|
||||||
name = "github.com/PuerkitoBio/urlesc"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "5bd2802263f21d8788851d5305584c82a5c75d7e"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:e3fc14e683e391975da7c151bd49b968977a6d83244dda5d0a5d0f171a1c23fd"
|
digest = "1:e3fc14e683e391975da7c151bd49b968977a6d83244dda5d0a5d0f171a1c23fd"
|
||||||
name = "github.com/Shopify/sarama"
|
name = "github.com/Shopify/sarama"
|
||||||
|
@ -642,16 +627,6 @@
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "30f82fa23fd844bd5bb1e5f216db87fd77b5eb43"
|
revision = "30f82fa23fd844bd5bb1e5f216db87fd77b5eb43"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:8731ec88b62435bdf4c35484309e6b7afd2e9caac8b0bb5661844d7d9ef70a64"
|
|
||||||
name = "github.com/emicklei/go-restful"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "89ef8af493ab468a45a42bb0d89a06fccdd2fb22"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:6f26e34204ddad638d25456c6443c2763aa38954558226722c253e08e9f491fa"
|
digest = "1:6f26e34204ddad638d25456c6443c2763aa38954558226722c253e08e9f491fa"
|
||||||
name = "github.com/exoscale/egoscale"
|
name = "github.com/exoscale/egoscale"
|
||||||
|
@ -829,34 +804,6 @@
|
||||||
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
|
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
|
||||||
version = "v0.3.0"
|
version = "v0.3.0"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:e90c60f901104f83cdcad5961d752541a0cd6a0bd996d7fd920bfe8f8c39f241"
|
|
||||||
name = "github.com/go-openapi/jsonpointer"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "46af16f9f7b149af66e5d1bd010e3574dc06de98"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:98abd61947ff5c7c6fcfec5473d02a4821ed3a2dd99a4fbfdb7925b0dd745546"
|
|
||||||
name = "github.com/go-openapi/jsonreference"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:9a247af322a3c4780ad9ba3517f517d93925dee18a7e420209f786f5110b1a28"
|
|
||||||
name = "github.com/go-openapi/spec"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "6aced65f8501fe1217321abf0749d354824ba2ff"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:4f5899c71c18d685e67cb365438b433f9d3e0c39f4c865f3d4fe80459b12ffd7"
|
|
||||||
name = "github.com/go-openapi/swag"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:00f1b1d722a012f54670ecc09ee92557d7314bfd9152a085ce91933c1c14b026"
|
digest = "1:00f1b1d722a012f54670ecc09ee92557d7314bfd9152a085ce91933c1c14b026"
|
||||||
name = "github.com/go-resty/resty"
|
name = "github.com/go-resty/resty"
|
||||||
|
@ -1072,14 +1019,6 @@
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "19f2c401e122352c047a84d6584dd51e2fb8fcc4"
|
revision = "19f2c401e122352c047a84d6584dd51e2fb8fcc4"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:b7f860847a1d71f925ba9385ed95f1ebc0abfeb418a78e219ab61f48fdfeffad"
|
|
||||||
name = "github.com/howeyc/gopass"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:45e66b20393507035c6a7d15bef5ffe8faf5b083621c1284d9824cc052776de5"
|
digest = "1:45e66b20393507035c6a7d15bef5ffe8faf5b083621c1284d9824cc052776de5"
|
||||||
name = "github.com/huandu/xstrings"
|
name = "github.com/huandu/xstrings"
|
||||||
|
@ -1141,12 +1080,12 @@
|
||||||
revision = "72f9bd7c4e0c2a40055ab3d0f09654f730cce982"
|
revision = "72f9bd7c4e0c2a40055ab3d0f09654f730cce982"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:63d8b364f0768ffda64b8e6e15c10535f3431e3c69d051dbb37f467ada02df98"
|
digest = "1:4e903242fe176238aaa469f59d7035f5abf2aa9acfefb8964ddd203651b574e9"
|
||||||
name = "github.com/json-iterator/go"
|
name = "github.com/json-iterator/go"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "28452fcdec4e44348d2af0d91d1e9e38da3a9e0a"
|
revision = "0ff49de124c6f76f8494e194af75bde0f1a49a29"
|
||||||
version = "1.0.5"
|
version = "v1.1.6"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:8b3234b10eacd5edea45bf0c13a585b608749da23f94aaf29b46d9ef8a8babf4"
|
digest = "1:8b3234b10eacd5edea45bf0c13a585b608749da23f94aaf29b46d9ef8a8babf4"
|
||||||
|
@ -1253,17 +1192,6 @@
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "c1c17f74874f2a5ea48bfb06b5459d4ef2689749"
|
revision = "c1c17f74874f2a5ea48bfb06b5459d4ef2689749"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:f9d15bd15966e80ec044ed83df29c267b87fbf646765939ee37e48287061e71b"
|
|
||||||
name = "github.com/mailru/easyjson"
|
|
||||||
packages = [
|
|
||||||
"buffer",
|
|
||||||
"jlexer",
|
|
||||||
"jwriter",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "d5b7844b561a7bc640052f1b935f7b800330d7e0"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:4953945f4fdc12cb7aa0263710534fb64b35a85e4047570fdf1cb03284055f0d"
|
digest = "1:4953945f4fdc12cb7aa0263710534fb64b35a85e4047570fdf1cb03284055f0d"
|
||||||
name = "github.com/mattn/go-colorable"
|
name = "github.com/mattn/go-colorable"
|
||||||
|
@ -1365,6 +1293,22 @@
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "63d60e9d0dbc60cf9164e6510889b0db6683d98c"
|
revision = "63d60e9d0dbc60cf9164e6510889b0db6683d98c"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f"
|
||||||
|
name = "github.com/modern-go/concurrent"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
|
||||||
|
version = "1.0.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:c6aca19413b13dc59c220ad7430329e2ec454cc310bc6d8de2c7e2b93c18a0f6"
|
||||||
|
name = "github.com/modern-go/reflect2"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
|
||||||
|
version = "1.0.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:7bb97a5f80a2429fa605e176e16cf682cbb5ac681f421a06efb4e3b8efae6e5f"
|
digest = "1:7bb97a5f80a2429fa605e176e16cf682cbb5ac681f421a06efb4e3b8efae6e5f"
|
||||||
name = "github.com/mvdan/xurls"
|
name = "github.com/mvdan/xurls"
|
||||||
|
@ -1929,25 +1873,18 @@
|
||||||
revision = "fff93fa7cd278d84afc205751523809c464168ab"
|
revision = "fff93fa7cd278d84afc205751523809c464168ab"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:6333c67358d261e0b96d6777a10f08ca074e2348a6c99d9dc1cebd0cf3736cb2"
|
digest = "1:ca9ebfc1200ca7423d9778dba9cdd463704753541c99dc4896f15e0b8b2bf1e8"
|
||||||
name = "golang.org/x/text"
|
name = "golang.org/x/text"
|
||||||
packages = [
|
packages = [
|
||||||
"cases",
|
|
||||||
"internal",
|
|
||||||
"internal/gen",
|
"internal/gen",
|
||||||
"internal/tag",
|
|
||||||
"internal/triegen",
|
"internal/triegen",
|
||||||
"internal/ucd",
|
"internal/ucd",
|
||||||
"language",
|
|
||||||
"runes",
|
|
||||||
"secure/bidirule",
|
"secure/bidirule",
|
||||||
"secure/precis",
|
|
||||||
"transform",
|
"transform",
|
||||||
"unicode/bidi",
|
"unicode/bidi",
|
||||||
"unicode/cldr",
|
"unicode/cldr",
|
||||||
"unicode/norm",
|
"unicode/norm",
|
||||||
"unicode/rangetable",
|
"unicode/rangetable",
|
||||||
"width",
|
|
||||||
]
|
]
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "4ee4af566555f5fbe026368b75596286a312663a"
|
revision = "4ee4af566555f5fbe026368b75596286a312663a"
|
||||||
|
@ -1959,6 +1896,27 @@
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "8be79e1e0910c292df4e79c241bb7e8f7e725959"
|
revision = "8be79e1e0910c292df4e79c241bb7e8f7e725959"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:e46d8e20161401a9cf8765dfa428494a3492a0b56fe114156b7da792bf41ba78"
|
||||||
|
name = "golang.org/x/tools"
|
||||||
|
packages = [
|
||||||
|
"go/ast/astutil",
|
||||||
|
"go/gcexportdata",
|
||||||
|
"go/internal/cgo",
|
||||||
|
"go/internal/gcimporter",
|
||||||
|
"go/internal/packagesdriver",
|
||||||
|
"go/packages",
|
||||||
|
"go/types/typeutil",
|
||||||
|
"imports",
|
||||||
|
"internal/fastwalk",
|
||||||
|
"internal/gopathwalk",
|
||||||
|
"internal/module",
|
||||||
|
"internal/semver",
|
||||||
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "589c23e65e65055d47b9ad4a99723bc389136265"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:da32ebe70dd3ec97d2df26281b08b18d05c2f12491ae79f389813f6c8d3006b3"
|
digest = "1:da32ebe70dd3ec97d2df26281b08b18d05c2f12491ae79f389813f6c8d3006b3"
|
||||||
|
@ -2105,7 +2063,7 @@
|
||||||
revision = "53feefa2559fb8dfa8d81baad31be332c97d6c77"
|
revision = "53feefa2559fb8dfa8d81baad31be332c97d6c77"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:85cc1b1a331f54ffd419ccd435426ee992603d4ba463b51790a312b59d1a09dc"
|
digest = "1:b1c6723e934087c2fa159e1c6a309c3c5c0b9a7d209c2ba6028f21240ebe7606"
|
||||||
name = "k8s.io/api"
|
name = "k8s.io/api"
|
||||||
packages = [
|
packages = [
|
||||||
"admissionregistration/v1alpha1",
|
"admissionregistration/v1alpha1",
|
||||||
|
@ -2132,17 +2090,18 @@
|
||||||
"rbac/v1alpha1",
|
"rbac/v1alpha1",
|
||||||
"rbac/v1beta1",
|
"rbac/v1beta1",
|
||||||
"scheduling/v1alpha1",
|
"scheduling/v1alpha1",
|
||||||
|
"scheduling/v1beta1",
|
||||||
"settings/v1alpha1",
|
"settings/v1alpha1",
|
||||||
"storage/v1",
|
"storage/v1",
|
||||||
"storage/v1alpha1",
|
"storage/v1alpha1",
|
||||||
"storage/v1beta1",
|
"storage/v1beta1",
|
||||||
]
|
]
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "af4bc157c3a209798fc897f6d4aaaaeb6c2e0d6a"
|
revision = "e86510ea3fe79b17eda7b8b3bb5cf8811d3af968"
|
||||||
version = "kubernetes-1.9.0"
|
version = "kubernetes-1.11.7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:4f4489c97d0f170a24dbb98fd47e904bae80abe7ece29f337a28cbc5470877b1"
|
digest = "1:1fa62171c3cf8abf9933ec87c1a00881f8f086a5928b2a8481c469ab67cb4104"
|
||||||
name = "k8s.io/apimachinery"
|
name = "k8s.io/apimachinery"
|
||||||
packages = [
|
packages = [
|
||||||
"pkg/api/errors",
|
"pkg/api/errors",
|
||||||
|
@ -2151,7 +2110,7 @@
|
||||||
"pkg/apis/meta/internalversion",
|
"pkg/apis/meta/internalversion",
|
||||||
"pkg/apis/meta/v1",
|
"pkg/apis/meta/v1",
|
||||||
"pkg/apis/meta/v1/unstructured",
|
"pkg/apis/meta/v1/unstructured",
|
||||||
"pkg/apis/meta/v1alpha1",
|
"pkg/apis/meta/v1beta1",
|
||||||
"pkg/conversion",
|
"pkg/conversion",
|
||||||
"pkg/conversion/queryparams",
|
"pkg/conversion/queryparams",
|
||||||
"pkg/fields",
|
"pkg/fields",
|
||||||
|
@ -2173,26 +2132,30 @@
|
||||||
"pkg/util/framer",
|
"pkg/util/framer",
|
||||||
"pkg/util/intstr",
|
"pkg/util/intstr",
|
||||||
"pkg/util/json",
|
"pkg/util/json",
|
||||||
|
"pkg/util/mergepatch",
|
||||||
"pkg/util/net",
|
"pkg/util/net",
|
||||||
"pkg/util/runtime",
|
"pkg/util/runtime",
|
||||||
"pkg/util/sets",
|
"pkg/util/sets",
|
||||||
|
"pkg/util/strategicpatch",
|
||||||
"pkg/util/validation",
|
"pkg/util/validation",
|
||||||
"pkg/util/validation/field",
|
"pkg/util/validation/field",
|
||||||
"pkg/util/wait",
|
"pkg/util/wait",
|
||||||
"pkg/util/yaml",
|
"pkg/util/yaml",
|
||||||
"pkg/version",
|
"pkg/version",
|
||||||
"pkg/watch",
|
"pkg/watch",
|
||||||
|
"third_party/forked/golang/json",
|
||||||
"third_party/forked/golang/reflect",
|
"third_party/forked/golang/reflect",
|
||||||
]
|
]
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "180eddb345a5be3a157cea1c624700ad5bd27b8f"
|
revision = "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
|
||||||
version = "kubernetes-1.9.0"
|
version = "kubernetes-1.11.7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:38c77bd3136157819bfeec7a3ce19b94a15f013f34499becb6ef3fdc206c660b"
|
digest = "1:b557d722202c63ce865634b8adc42b7d283b7d71bd52c7becf525abc04b8bed9"
|
||||||
name = "k8s.io/client-go"
|
name = "k8s.io/client-go"
|
||||||
packages = [
|
packages = [
|
||||||
"discovery",
|
"discovery",
|
||||||
|
"discovery/fake",
|
||||||
"informers",
|
"informers",
|
||||||
"informers/admissionregistration",
|
"informers/admissionregistration",
|
||||||
"informers/admissionregistration/v1alpha1",
|
"informers/admissionregistration/v1alpha1",
|
||||||
|
@ -2227,6 +2190,7 @@
|
||||||
"informers/rbac/v1beta1",
|
"informers/rbac/v1beta1",
|
||||||
"informers/scheduling",
|
"informers/scheduling",
|
||||||
"informers/scheduling/v1alpha1",
|
"informers/scheduling/v1alpha1",
|
||||||
|
"informers/scheduling/v1beta1",
|
||||||
"informers/settings",
|
"informers/settings",
|
||||||
"informers/settings/v1alpha1",
|
"informers/settings/v1alpha1",
|
||||||
"informers/storage",
|
"informers/storage",
|
||||||
|
@ -2259,6 +2223,7 @@
|
||||||
"kubernetes/typed/rbac/v1alpha1",
|
"kubernetes/typed/rbac/v1alpha1",
|
||||||
"kubernetes/typed/rbac/v1beta1",
|
"kubernetes/typed/rbac/v1beta1",
|
||||||
"kubernetes/typed/scheduling/v1alpha1",
|
"kubernetes/typed/scheduling/v1alpha1",
|
||||||
|
"kubernetes/typed/scheduling/v1beta1",
|
||||||
"kubernetes/typed/settings/v1alpha1",
|
"kubernetes/typed/settings/v1alpha1",
|
||||||
"kubernetes/typed/storage/v1",
|
"kubernetes/typed/storage/v1",
|
||||||
"kubernetes/typed/storage/v1alpha1",
|
"kubernetes/typed/storage/v1alpha1",
|
||||||
|
@ -2283,13 +2248,19 @@
|
||||||
"listers/rbac/v1alpha1",
|
"listers/rbac/v1alpha1",
|
||||||
"listers/rbac/v1beta1",
|
"listers/rbac/v1beta1",
|
||||||
"listers/scheduling/v1alpha1",
|
"listers/scheduling/v1alpha1",
|
||||||
|
"listers/scheduling/v1beta1",
|
||||||
"listers/settings/v1alpha1",
|
"listers/settings/v1alpha1",
|
||||||
"listers/storage/v1",
|
"listers/storage/v1",
|
||||||
"listers/storage/v1alpha1",
|
"listers/storage/v1alpha1",
|
||||||
"listers/storage/v1beta1",
|
"listers/storage/v1beta1",
|
||||||
|
"pkg/apis/clientauthentication",
|
||||||
|
"pkg/apis/clientauthentication/v1alpha1",
|
||||||
|
"pkg/apis/clientauthentication/v1beta1",
|
||||||
"pkg/version",
|
"pkg/version",
|
||||||
|
"plugin/pkg/client/auth/exec",
|
||||||
"rest",
|
"rest",
|
||||||
"rest/watch",
|
"rest/watch",
|
||||||
|
"testing",
|
||||||
"tools/auth",
|
"tools/auth",
|
||||||
"tools/cache",
|
"tools/cache",
|
||||||
"tools/clientcmd",
|
"tools/clientcmd",
|
||||||
|
@ -2302,21 +2273,76 @@
|
||||||
"transport",
|
"transport",
|
||||||
"util/buffer",
|
"util/buffer",
|
||||||
"util/cert",
|
"util/cert",
|
||||||
|
"util/connrotation",
|
||||||
"util/flowcontrol",
|
"util/flowcontrol",
|
||||||
"util/homedir",
|
"util/homedir",
|
||||||
"util/integer",
|
"util/integer",
|
||||||
|
"util/retry",
|
||||||
]
|
]
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "78700dec6369ba22221b72770783300f143df150"
|
revision = "7d04d0e2a0a1a4d4a1cd6baa432a2301492e4e65"
|
||||||
version = "v6.0.0"
|
version = "v8.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:7320216e50843bfc1092d20b535a20683abc967e0eba38441c85c3265da10a34"
|
||||||
|
name = "k8s.io/code-generator"
|
||||||
|
packages = [
|
||||||
|
"cmd/client-gen",
|
||||||
|
"cmd/client-gen/args",
|
||||||
|
"cmd/client-gen/generators",
|
||||||
|
"cmd/client-gen/generators/fake",
|
||||||
|
"cmd/client-gen/generators/scheme",
|
||||||
|
"cmd/client-gen/generators/util",
|
||||||
|
"cmd/client-gen/path",
|
||||||
|
"cmd/client-gen/types",
|
||||||
|
"cmd/deepcopy-gen",
|
||||||
|
"cmd/deepcopy-gen/args",
|
||||||
|
"cmd/defaulter-gen",
|
||||||
|
"cmd/defaulter-gen/args",
|
||||||
|
"cmd/informer-gen",
|
||||||
|
"cmd/informer-gen/args",
|
||||||
|
"cmd/informer-gen/generators",
|
||||||
|
"cmd/lister-gen",
|
||||||
|
"cmd/lister-gen/args",
|
||||||
|
"cmd/lister-gen/generators",
|
||||||
|
"pkg/util",
|
||||||
|
]
|
||||||
|
pruneopts = "T"
|
||||||
|
revision = "f8cba74510f397bac80157a6c4ccb0ffbc31b9d0"
|
||||||
|
version = "kubernetes-1.11.7"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:f5487c07872bdb7c40ffe629430b2fa815f9eca0d2c02bb9e866962eb38a0e70"
|
digest = "1:61024ed77a53ac618effed55043bf6a9afbdeb64136bd6a5b0c992d4c0363766"
|
||||||
name = "k8s.io/kube-openapi"
|
name = "k8s.io/gengo"
|
||||||
packages = ["pkg/common"]
|
packages = [
|
||||||
|
"args",
|
||||||
|
"examples/deepcopy-gen/generators",
|
||||||
|
"examples/defaulter-gen/generators",
|
||||||
|
"examples/set-gen/sets",
|
||||||
|
"generator",
|
||||||
|
"namer",
|
||||||
|
"parser",
|
||||||
|
"types",
|
||||||
|
]
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "275e2ce91dec4c05a4094a7b1daee5560b555ac9"
|
revision = "0689ccc1d7d65d9dd1bedcc3b0b1ed7df91ba266"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:c263611800c3a97991dbcf9d3bc4de390f6224aaa8ca0a7226a9d734f65a416a"
|
||||||
|
name = "k8s.io/klog"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "71442cd4037d612096940ceb0f3fec3f7fff66e0"
|
||||||
|
version = "v0.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:03a96603922fc1f6895ae083e1e16d943b55ef0656b56965351bd87e7d90485f"
|
||||||
|
name = "k8s.io/kube-openapi"
|
||||||
|
packages = ["pkg/util/proto"]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "15615b16d372105f0c69ff47dfe7402926a65aaa"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
|
@ -2453,7 +2479,6 @@
|
||||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer",
|
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer",
|
||||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer",
|
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer",
|
||||||
"gopkg.in/fsnotify.v1",
|
"gopkg.in/fsnotify.v1",
|
||||||
"gopkg.in/yaml.v2",
|
|
||||||
"k8s.io/api/core/v1",
|
"k8s.io/api/core/v1",
|
||||||
"k8s.io/api/extensions/v1beta1",
|
"k8s.io/api/extensions/v1beta1",
|
||||||
"k8s.io/apimachinery/pkg/api/errors",
|
"k8s.io/apimachinery/pkg/api/errors",
|
||||||
|
@ -2461,14 +2486,25 @@
|
||||||
"k8s.io/apimachinery/pkg/labels",
|
"k8s.io/apimachinery/pkg/labels",
|
||||||
"k8s.io/apimachinery/pkg/runtime",
|
"k8s.io/apimachinery/pkg/runtime",
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema",
|
"k8s.io/apimachinery/pkg/runtime/schema",
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer",
|
||||||
"k8s.io/apimachinery/pkg/types",
|
"k8s.io/apimachinery/pkg/types",
|
||||||
"k8s.io/apimachinery/pkg/util/intstr",
|
"k8s.io/apimachinery/pkg/util/intstr",
|
||||||
|
"k8s.io/apimachinery/pkg/watch",
|
||||||
|
"k8s.io/client-go/discovery",
|
||||||
|
"k8s.io/client-go/discovery/fake",
|
||||||
"k8s.io/client-go/informers",
|
"k8s.io/client-go/informers",
|
||||||
"k8s.io/client-go/kubernetes",
|
"k8s.io/client-go/kubernetes",
|
||||||
"k8s.io/client-go/kubernetes/scheme",
|
"k8s.io/client-go/kubernetes/scheme",
|
||||||
"k8s.io/client-go/rest",
|
"k8s.io/client-go/rest",
|
||||||
|
"k8s.io/client-go/testing",
|
||||||
"k8s.io/client-go/tools/cache",
|
"k8s.io/client-go/tools/cache",
|
||||||
"k8s.io/client-go/tools/clientcmd",
|
"k8s.io/client-go/tools/clientcmd",
|
||||||
|
"k8s.io/client-go/util/flowcontrol",
|
||||||
|
"k8s.io/code-generator/cmd/client-gen",
|
||||||
|
"k8s.io/code-generator/cmd/deepcopy-gen",
|
||||||
|
"k8s.io/code-generator/cmd/defaulter-gen",
|
||||||
|
"k8s.io/code-generator/cmd/informer-gen",
|
||||||
|
"k8s.io/code-generator/cmd/lister-gen",
|
||||||
]
|
]
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
26
Gopkg.toml
26
Gopkg.toml
|
@ -19,10 +19,22 @@
|
||||||
# name = "github.com/x/y"
|
# name = "github.com/x/y"
|
||||||
# version = "2.4.0"
|
# version = "2.4.0"
|
||||||
|
|
||||||
|
required = [
|
||||||
|
"k8s.io/code-generator/cmd/client-gen",
|
||||||
|
"k8s.io/code-generator/cmd/deepcopy-gen",
|
||||||
|
"k8s.io/code-generator/cmd/defaulter-gen",
|
||||||
|
"k8s.io/code-generator/cmd/lister-gen",
|
||||||
|
"k8s.io/code-generator/cmd/informer-gen",
|
||||||
|
]
|
||||||
|
|
||||||
[prune]
|
[prune]
|
||||||
non-go = true
|
non-go = true
|
||||||
go-tests = true
|
go-tests = true
|
||||||
unused-packages = true
|
unused-packages = true
|
||||||
|
[[prune.project]]
|
||||||
|
name = "k8s.io/code-generator"
|
||||||
|
non-go = false
|
||||||
|
unused-packages = false
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
@ -193,15 +205,23 @@
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "k8s.io/client-go"
|
name = "k8s.io/client-go"
|
||||||
version = "6.0.0"
|
version = "8.0.0" # 8.0.0
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "k8s.io/code-generator"
|
||||||
|
version = "kubernetes-1.11.7"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "k8s.io/api"
|
name = "k8s.io/api"
|
||||||
version = "kubernetes-1.9.0"
|
version = "kubernetes-1.11.7" # "kubernetes-1.11.7"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "k8s.io/apimachinery"
|
name = "k8s.io/apimachinery"
|
||||||
version = "kubernetes-1.9.0"
|
version = "kubernetes-1.11.7" # "kubernetes-1.11.7"
|
||||||
|
|
||||||
|
[[override]]
|
||||||
|
name = "github.com/json-iterator/go"
|
||||||
|
version = "1.1.6"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -113,6 +113,9 @@ generate-webui: build-webui
|
||||||
echo 'For more informations show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
|
echo 'For more informations show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
generate-crd:
|
||||||
|
./script/update-generated-crd-code.sh
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
script/validate-lint
|
script/validate-lint
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"github.com/containous/traefik/ping"
|
"github.com/containous/traefik/ping"
|
||||||
"github.com/containous/traefik/provider/docker"
|
"github.com/containous/traefik/provider/docker"
|
||||||
"github.com/containous/traefik/provider/file"
|
"github.com/containous/traefik/provider/file"
|
||||||
"github.com/containous/traefik/provider/kubernetes"
|
"github.com/containous/traefik/provider/kubernetes/ingress"
|
||||||
"github.com/containous/traefik/provider/marathon"
|
"github.com/containous/traefik/provider/marathon"
|
||||||
"github.com/containous/traefik/provider/rest"
|
"github.com/containous/traefik/provider/rest"
|
||||||
"github.com/containous/traefik/tracing/datadog"
|
"github.com/containous/traefik/tracing/datadog"
|
||||||
|
@ -214,7 +214,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||||
defaultBoltDb.Prefix = "/traefik"
|
defaultBoltDb.Prefix = "/traefik"
|
||||||
|
|
||||||
// default Kubernetes
|
// default Kubernetes
|
||||||
var defaultKubernetes kubernetes.Provider
|
var defaultKubernetes ingress.Provider
|
||||||
defaultKubernetes.Watch = true
|
defaultKubernetes.Watch = true
|
||||||
|
|
||||||
// default Mesos
|
// default Mesos
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/containous/traefik/old/provider/ecs"
|
"github.com/containous/traefik/old/provider/ecs"
|
||||||
oldtypes "github.com/containous/traefik/old/types"
|
oldtypes "github.com/containous/traefik/old/types"
|
||||||
"github.com/containous/traefik/provider/aggregator"
|
"github.com/containous/traefik/provider/aggregator"
|
||||||
"github.com/containous/traefik/provider/kubernetes"
|
"github.com/containous/traefik/provider/kubernetes/k8s"
|
||||||
"github.com/containous/traefik/safe"
|
"github.com/containous/traefik/safe"
|
||||||
"github.com/containous/traefik/server"
|
"github.com/containous/traefik/server"
|
||||||
"github.com/containous/traefik/server/router"
|
"github.com/containous/traefik/server/router"
|
||||||
|
@ -116,7 +116,7 @@ Complete documentation is available at https://traefik.io`,
|
||||||
f.AddParser(reflect.SliceOf(reflect.TypeOf("")), &sliceOfStrings{})
|
f.AddParser(reflect.SliceOf(reflect.TypeOf("")), &sliceOfStrings{})
|
||||||
f.AddParser(reflect.TypeOf(traefiktls.FilesOrContents{}), &traefiktls.FilesOrContents{})
|
f.AddParser(reflect.TypeOf(traefiktls.FilesOrContents{}), &traefiktls.FilesOrContents{})
|
||||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
||||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
f.AddParser(reflect.TypeOf(k8s.Namespaces{}), &k8s.Namespaces{})
|
||||||
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
||||||
f.AddParser(reflect.TypeOf([]types.Domain{}), &types.Domains{})
|
f.AddParser(reflect.TypeOf([]types.Domain{}), &types.Domains{})
|
||||||
f.AddParser(reflect.TypeOf(types.DNSResolvers{}), &types.DNSResolvers{})
|
f.AddParser(reflect.TypeOf(types.DNSResolvers{}), &types.DNSResolvers{})
|
||||||
|
|
|
@ -34,7 +34,7 @@ type TCPRouter struct {
|
||||||
|
|
||||||
// RouterTCPTLSConfig holds the TLS configuration for a router
|
// RouterTCPTLSConfig holds the TLS configuration for a router
|
||||||
type RouterTCPTLSConfig struct {
|
type RouterTCPTLSConfig struct {
|
||||||
Passthrough bool `json:"passthrough,omitempty" toml:"passthrough,omitzero"`
|
Passthrough bool `json:"passthrough" toml:"passthrough,omitzero"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadBalancerService holds the LoadBalancerService configuration.
|
// LoadBalancerService holds the LoadBalancerService configuration.
|
||||||
|
@ -119,16 +119,6 @@ type HealthCheck struct {
|
||||||
Headers map[string]string `json:"headers,omitempty" toml:",omitempty"`
|
Headers map[string]string `json:"headers,omitempty" toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientTLS holds the TLS specific configurations as client
|
|
||||||
// CA, Cert and Key can be either path or file contents.
|
|
||||||
type ClientTLS struct {
|
|
||||||
CA string `description:"TLS CA" json:"ca,omitempty"`
|
|
||||||
CAOptional bool `description:"TLS CA.Optional" json:"caOptional,omitempty"`
|
|
||||||
Cert string `description:"TLS cert" json:"cert,omitempty"`
|
|
||||||
Key string `description:"TLS key" json:"key,omitempty"`
|
|
||||||
InsecureSkipVerify bool `description:"TLS insecure skip verify" json:"insecureSkipVerify,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateTLSConfig creates a TLS config from ClientTLS structures.
|
// CreateTLSConfig creates a TLS config from ClientTLS structures.
|
||||||
func (clientTLS *ClientTLS) CreateTLSConfig() (*tls.Config, error) {
|
func (clientTLS *ClientTLS) CreateTLSConfig() (*tls.Config, error) {
|
||||||
if clientTLS == nil {
|
if clientTLS == nil {
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"github.com/containous/traefik/ip"
|
"github.com/containous/traefik/ip"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Middleware holds the Middleware configuration.
|
// Middleware holds the Middleware configuration.
|
||||||
type Middleware struct {
|
type Middleware struct {
|
||||||
AddPrefix *AddPrefix `json:"addPrefix,omitempty"`
|
AddPrefix *AddPrefix `json:"addPrefix,omitempty"`
|
||||||
|
@ -30,11 +32,15 @@ type Middleware struct {
|
||||||
Retry *Retry `json:"retry,omitempty"`
|
Retry *Retry `json:"retry,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// AddPrefix holds the AddPrefix configuration.
|
// AddPrefix holds the AddPrefix configuration.
|
||||||
type AddPrefix struct {
|
type AddPrefix struct {
|
||||||
Prefix string `json:"prefix,omitempty"`
|
Prefix string `json:"prefix,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Auth holds the authentication configuration (BASIC, DIGEST, users).
|
// Auth holds the authentication configuration (BASIC, DIGEST, users).
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
Basic *BasicAuth `json:"basic,omitempty" export:"true"`
|
Basic *BasicAuth `json:"basic,omitempty" export:"true"`
|
||||||
|
@ -42,6 +48,8 @@ type Auth struct {
|
||||||
Forward *ForwardAuth `json:"forward,omitempty" export:"true"`
|
Forward *ForwardAuth `json:"forward,omitempty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// BasicAuth holds the HTTP basic authentication configuration.
|
// BasicAuth holds the HTTP basic authentication configuration.
|
||||||
type BasicAuth struct {
|
type BasicAuth struct {
|
||||||
Users `json:"users,omitempty" mapstructure:","`
|
Users `json:"users,omitempty" mapstructure:","`
|
||||||
|
@ -51,6 +59,8 @@ type BasicAuth struct {
|
||||||
HeaderField string `json:"headerField,omitempty" export:"true"`
|
HeaderField string `json:"headerField,omitempty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Buffering holds the request/response buffering configuration.
|
// Buffering holds the request/response buffering configuration.
|
||||||
type Buffering struct {
|
type Buffering struct {
|
||||||
MaxRequestBodyBytes int64 `json:"maxRequestBodyBytes,omitempty"`
|
MaxRequestBodyBytes int64 `json:"maxRequestBodyBytes,omitempty"`
|
||||||
|
@ -60,19 +70,27 @@ type Buffering struct {
|
||||||
RetryExpression string `json:"retryExpression,omitempty"`
|
RetryExpression string `json:"retryExpression,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Chain holds a chain of middlewares
|
// Chain holds a chain of middlewares
|
||||||
type Chain struct {
|
type Chain struct {
|
||||||
Middlewares []string `json:"middlewares"`
|
Middlewares []string `json:"middlewares"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// CircuitBreaker holds the circuit breaker configuration.
|
// CircuitBreaker holds the circuit breaker configuration.
|
||||||
type CircuitBreaker struct {
|
type CircuitBreaker struct {
|
||||||
Expression string `json:"expression,omitempty"`
|
Expression string `json:"expression,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Compress holds the compress configuration.
|
// Compress holds the compress configuration.
|
||||||
type Compress struct{}
|
type Compress struct{}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// DigestAuth holds the Digest HTTP authentication configuration.
|
// DigestAuth holds the Digest HTTP authentication configuration.
|
||||||
type DigestAuth struct {
|
type DigestAuth struct {
|
||||||
Users `json:"users,omitempty" mapstructure:","`
|
Users `json:"users,omitempty" mapstructure:","`
|
||||||
|
@ -82,6 +100,8 @@ type DigestAuth struct {
|
||||||
HeaderField string `json:"headerField,omitempty" export:"true"`
|
HeaderField string `json:"headerField,omitempty" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// ErrorPage holds the custom error page configuration.
|
// ErrorPage holds the custom error page configuration.
|
||||||
type ErrorPage struct {
|
type ErrorPage struct {
|
||||||
Status []string `json:"status,omitempty"`
|
Status []string `json:"status,omitempty"`
|
||||||
|
@ -89,6 +109,8 @@ type ErrorPage struct {
|
||||||
Query string `json:"query,omitempty"`
|
Query string `json:"query,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// ForwardAuth holds the http forward authentication configuration.
|
// ForwardAuth holds the http forward authentication configuration.
|
||||||
type ForwardAuth struct {
|
type ForwardAuth struct {
|
||||||
Address string `description:"Authentication server address" json:"address,omitempty"`
|
Address string `description:"Authentication server address" json:"address,omitempty"`
|
||||||
|
@ -97,6 +119,8 @@ type ForwardAuth struct {
|
||||||
AuthResponseHeaders []string `description:"Headers to be forwarded from auth response" json:"authResponseHeaders,omitempty"`
|
AuthResponseHeaders []string `description:"Headers to be forwarded from auth response" json:"authResponseHeaders,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Headers holds the custom header configuration.
|
// Headers holds the custom header configuration.
|
||||||
type Headers struct {
|
type Headers struct {
|
||||||
CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty"`
|
CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty"`
|
||||||
|
@ -154,6 +178,8 @@ func (h *Headers) HasSecureHeadersDefined() bool {
|
||||||
h.IsDevelopment)
|
h.IsDevelopment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// IPStrategy holds the ip strategy configuration.
|
// IPStrategy holds the ip strategy configuration.
|
||||||
type IPStrategy struct {
|
type IPStrategy struct {
|
||||||
Depth int `json:"depth,omitempty" export:"true"`
|
Depth int `json:"depth,omitempty" export:"true"`
|
||||||
|
@ -188,12 +214,16 @@ func (s *IPStrategy) Get() (ip.Strategy, error) {
|
||||||
return &ip.RemoteAddrStrategy{}, nil
|
return &ip.RemoteAddrStrategy{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// IPWhiteList holds the ip white list configuration.
|
// IPWhiteList holds the ip white list configuration.
|
||||||
type IPWhiteList struct {
|
type IPWhiteList struct {
|
||||||
SourceRange []string `json:"sourceRange,omitempty"`
|
SourceRange []string `json:"sourceRange,omitempty"`
|
||||||
IPStrategy *IPStrategy `json:"ipStrategy,omitempty" label:"allowEmpty"`
|
IPStrategy *IPStrategy `json:"ipStrategy,omitempty" label:"allowEmpty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// MaxConn holds maximum connection configuration.
|
// MaxConn holds maximum connection configuration.
|
||||||
type MaxConn struct {
|
type MaxConn struct {
|
||||||
Amount int64 `json:"amount,omitempty"`
|
Amount int64 `json:"amount,omitempty"`
|
||||||
|
@ -205,12 +235,16 @@ func (m *MaxConn) SetDefaults() {
|
||||||
m.ExtractorFunc = "request.host"
|
m.ExtractorFunc = "request.host"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// PassTLSClientCert holds the TLS client cert headers configuration.
|
// PassTLSClientCert holds the TLS client cert headers configuration.
|
||||||
type PassTLSClientCert struct {
|
type PassTLSClientCert struct {
|
||||||
PEM bool `description:"Enable header with escaped client pem" json:"pem"`
|
PEM bool `description:"Enable header with escaped client pem" json:"pem"`
|
||||||
Info *TLSClientCertificateInfo `description:"Enable header with configured client cert info" json:"info,omitempty"`
|
Info *TLSClientCertificateInfo `description:"Enable header with configured client cert info" json:"info,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Rate holds the rate limiting configuration for a specific time period.
|
// Rate holds the rate limiting configuration for a specific time period.
|
||||||
type Rate struct {
|
type Rate struct {
|
||||||
Period parse.Duration `json:"period,omitempty"`
|
Period parse.Duration `json:"period,omitempty"`
|
||||||
|
@ -218,6 +252,8 @@ type Rate struct {
|
||||||
Burst int64 `json:"burst,omitempty"`
|
Burst int64 `json:"burst,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// RateLimit holds the rate limiting configuration for a given frontend.
|
// RateLimit holds the rate limiting configuration for a given frontend.
|
||||||
type RateLimit struct {
|
type RateLimit struct {
|
||||||
RateSet map[string]*Rate `json:"rateset,omitempty"`
|
RateSet map[string]*Rate `json:"rateset,omitempty"`
|
||||||
|
@ -230,6 +266,8 @@ func (r *RateLimit) SetDefaults() {
|
||||||
r.ExtractorFunc = "request.host"
|
r.ExtractorFunc = "request.host"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// RedirectRegex holds the redirection configuration.
|
// RedirectRegex holds the redirection configuration.
|
||||||
type RedirectRegex struct {
|
type RedirectRegex struct {
|
||||||
Regex string `json:"regex,omitempty"`
|
Regex string `json:"regex,omitempty"`
|
||||||
|
@ -237,6 +275,8 @@ type RedirectRegex struct {
|
||||||
Permanent bool `json:"permanent,omitempty"`
|
Permanent bool `json:"permanent,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// RedirectScheme holds the scheme redirection configuration.
|
// RedirectScheme holds the scheme redirection configuration.
|
||||||
type RedirectScheme struct {
|
type RedirectScheme struct {
|
||||||
Scheme string `json:"scheme,omitempty"`
|
Scheme string `json:"scheme,omitempty"`
|
||||||
|
@ -244,32 +284,44 @@ type RedirectScheme struct {
|
||||||
Permanent bool `json:"permanent,omitempty"`
|
Permanent bool `json:"permanent,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// ReplacePath holds the ReplacePath configuration.
|
// ReplacePath holds the ReplacePath configuration.
|
||||||
type ReplacePath struct {
|
type ReplacePath struct {
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// ReplacePathRegex holds the ReplacePathRegex configuration.
|
// ReplacePathRegex holds the ReplacePathRegex configuration.
|
||||||
type ReplacePathRegex struct {
|
type ReplacePathRegex struct {
|
||||||
Regex string `json:"regex,omitempty"`
|
Regex string `json:"regex,omitempty"`
|
||||||
Replacement string `json:"replacement,omitempty"`
|
Replacement string `json:"replacement,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Retry holds the retry configuration.
|
// Retry holds the retry configuration.
|
||||||
type Retry struct {
|
type Retry struct {
|
||||||
Attempts int `description:"Number of attempts" export:"true"`
|
Attempts int `description:"Number of attempts" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// StripPrefix holds the StripPrefix configuration.
|
// StripPrefix holds the StripPrefix configuration.
|
||||||
type StripPrefix struct {
|
type StripPrefix struct {
|
||||||
Prefixes []string `json:"prefixes,omitempty"`
|
Prefixes []string `json:"prefixes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// StripPrefixRegex holds the StripPrefixRegex configuration.
|
// StripPrefixRegex holds the StripPrefixRegex configuration.
|
||||||
type StripPrefixRegex struct {
|
type StripPrefixRegex struct {
|
||||||
Regex []string `json:"regex,omitempty"`
|
Regex []string `json:"regex,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// TLSClientCertificateInfo holds the client TLS certificate info configuration.
|
// TLSClientCertificateInfo holds the client TLS certificate info configuration.
|
||||||
type TLSClientCertificateInfo struct {
|
type TLSClientCertificateInfo struct {
|
||||||
NotAfter bool `description:"Add NotAfter info in header" json:"notAfter"`
|
NotAfter bool `description:"Add NotAfter info in header" json:"notAfter"`
|
||||||
|
@ -279,6 +331,8 @@ type TLSClientCertificateInfo struct {
|
||||||
Issuer *TLSCLientCertificateDNInfo `description:"Add Issuer info in header" json:"issuer,omitempty"`
|
Issuer *TLSCLientCertificateDNInfo `description:"Add Issuer info in header" json:"issuer,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// TLSCLientCertificateDNInfo holds the client TLS certificate distinguished name info configuration
|
// TLSCLientCertificateDNInfo holds the client TLS certificate distinguished name info configuration
|
||||||
// cf https://tools.ietf.org/html/rfc3739
|
// cf https://tools.ietf.org/html/rfc3739
|
||||||
type TLSCLientCertificateDNInfo struct {
|
type TLSCLientCertificateDNInfo struct {
|
||||||
|
@ -291,5 +345,19 @@ type TLSCLientCertificateDNInfo struct {
|
||||||
DomainComponent bool `description:"Add Domain Component info in header" json:"domainComponent"`
|
DomainComponent bool `description:"Add Domain Component info in header" json:"domainComponent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
// Users holds a list of users
|
// Users holds a list of users
|
||||||
type Users []string
|
type Users []string
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
||||||
|
// ClientTLS holds the TLS specific configurations as client
|
||||||
|
// CA, Cert and Key can be either path or file contents.
|
||||||
|
type ClientTLS struct {
|
||||||
|
CA string `description:"TLS CA" json:"ca,omitempty"`
|
||||||
|
CAOptional bool `description:"TLS CA.Optional" json:"caOptional,omitempty"`
|
||||||
|
Cert string `description:"TLS cert" json:"cert,omitempty"`
|
||||||
|
Key string `description:"TLS key" json:"key,omitempty"`
|
||||||
|
InsecureSkipVerify bool `description:"TLS insecure skip verify" json:"insecureSkipVerify,omitempty"`
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ import (
|
||||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||||
"github.com/containous/traefik/provider/docker"
|
"github.com/containous/traefik/provider/docker"
|
||||||
"github.com/containous/traefik/provider/file"
|
"github.com/containous/traefik/provider/file"
|
||||||
"github.com/containous/traefik/provider/kubernetes"
|
"github.com/containous/traefik/provider/kubernetes/crd"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/ingress"
|
||||||
"github.com/containous/traefik/provider/marathon"
|
"github.com/containous/traefik/provider/marathon"
|
||||||
"github.com/containous/traefik/provider/rest"
|
"github.com/containous/traefik/provider/rest"
|
||||||
"github.com/containous/traefik/tls"
|
"github.com/containous/traefik/tls"
|
||||||
|
@ -137,7 +138,8 @@ type Providers struct {
|
||||||
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
|
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
|
||||||
Zookeeper *zk.Provider `description:"Enable Zookeeper backend with default settings" export:"true"`
|
Zookeeper *zk.Provider `description:"Enable Zookeeper backend with default settings" export:"true"`
|
||||||
Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
|
Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
|
||||||
Kubernetes *kubernetes.Provider `description:"Enable Kubernetes backend with default settings" export:"true"`
|
Kubernetes *ingress.Provider `description:"Enable Kubernetes backend with default settings" export:"true"`
|
||||||
|
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings" export:"true"`
|
||||||
Mesos *mesos.Provider `description:"Enable Mesos backend with default settings" export:"true"`
|
Mesos *mesos.Provider `description:"Enable Mesos backend with default settings" export:"true"`
|
||||||
Eureka *eureka.Provider `description:"Enable Eureka backend with default settings" export:"true"`
|
Eureka *eureka.Provider `description:"Enable Eureka backend with default settings" export:"true"`
|
||||||
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
|
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
|
||||||
|
|
733
config/zz_generated.deepcopy.go
Normal file
733
config/zz_generated.deepcopy.go
Normal file
|
@ -0,0 +1,733 @@
|
||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *AddPrefix) DeepCopyInto(out *AddPrefix) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddPrefix.
|
||||||
|
func (in *AddPrefix) DeepCopy() *AddPrefix {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(AddPrefix)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Auth) DeepCopyInto(out *Auth) {
|
||||||
|
*out = *in
|
||||||
|
if in.Basic != nil {
|
||||||
|
in, out := &in.Basic, &out.Basic
|
||||||
|
*out = new(BasicAuth)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.Digest != nil {
|
||||||
|
in, out := &in.Digest, &out.Digest
|
||||||
|
*out = new(DigestAuth)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.Forward != nil {
|
||||||
|
in, out := &in.Forward, &out.Forward
|
||||||
|
*out = new(ForwardAuth)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Auth.
|
||||||
|
func (in *Auth) DeepCopy() *Auth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Auth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *BasicAuth) DeepCopyInto(out *BasicAuth) {
|
||||||
|
*out = *in
|
||||||
|
if in.Users != nil {
|
||||||
|
in, out := &in.Users, &out.Users
|
||||||
|
*out = make(Users, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuth.
|
||||||
|
func (in *BasicAuth) DeepCopy() *BasicAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(BasicAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Buffering) DeepCopyInto(out *Buffering) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Buffering.
|
||||||
|
func (in *Buffering) DeepCopy() *Buffering {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Buffering)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Chain) DeepCopyInto(out *Chain) {
|
||||||
|
*out = *in
|
||||||
|
if in.Middlewares != nil {
|
||||||
|
in, out := &in.Middlewares, &out.Middlewares
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Chain.
|
||||||
|
func (in *Chain) DeepCopy() *Chain {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Chain)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *CircuitBreaker) DeepCopyInto(out *CircuitBreaker) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CircuitBreaker.
|
||||||
|
func (in *CircuitBreaker) DeepCopy() *CircuitBreaker {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(CircuitBreaker)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ClientTLS) DeepCopyInto(out *ClientTLS) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTLS.
|
||||||
|
func (in *ClientTLS) DeepCopy() *ClientTLS {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ClientTLS)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Compress) DeepCopyInto(out *Compress) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Compress.
|
||||||
|
func (in *Compress) DeepCopy() *Compress {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Compress)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *DigestAuth) DeepCopyInto(out *DigestAuth) {
|
||||||
|
*out = *in
|
||||||
|
if in.Users != nil {
|
||||||
|
in, out := &in.Users, &out.Users
|
||||||
|
*out = make(Users, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DigestAuth.
|
||||||
|
func (in *DigestAuth) DeepCopy() *DigestAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(DigestAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ErrorPage) DeepCopyInto(out *ErrorPage) {
|
||||||
|
*out = *in
|
||||||
|
if in.Status != nil {
|
||||||
|
in, out := &in.Status, &out.Status
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ErrorPage.
|
||||||
|
func (in *ErrorPage) DeepCopy() *ErrorPage {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ErrorPage)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) {
|
||||||
|
*out = *in
|
||||||
|
if in.TLS != nil {
|
||||||
|
in, out := &in.TLS, &out.TLS
|
||||||
|
*out = new(ClientTLS)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.AuthResponseHeaders != nil {
|
||||||
|
in, out := &in.AuthResponseHeaders, &out.AuthResponseHeaders
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardAuth.
|
||||||
|
func (in *ForwardAuth) DeepCopy() *ForwardAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ForwardAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Headers) DeepCopyInto(out *Headers) {
|
||||||
|
*out = *in
|
||||||
|
if in.CustomRequestHeaders != nil {
|
||||||
|
in, out := &in.CustomRequestHeaders, &out.CustomRequestHeaders
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.CustomResponseHeaders != nil {
|
||||||
|
in, out := &in.CustomResponseHeaders, &out.CustomResponseHeaders
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.AllowedHosts != nil {
|
||||||
|
in, out := &in.AllowedHosts, &out.AllowedHosts
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.HostsProxyHeaders != nil {
|
||||||
|
in, out := &in.HostsProxyHeaders, &out.HostsProxyHeaders
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.SSLProxyHeaders != nil {
|
||||||
|
in, out := &in.SSLProxyHeaders, &out.SSLProxyHeaders
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Headers.
|
||||||
|
func (in *Headers) DeepCopy() *Headers {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Headers)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *IPStrategy) DeepCopyInto(out *IPStrategy) {
|
||||||
|
*out = *in
|
||||||
|
if in.ExcludedIPs != nil {
|
||||||
|
in, out := &in.ExcludedIPs, &out.ExcludedIPs
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPStrategy.
|
||||||
|
func (in *IPStrategy) DeepCopy() *IPStrategy {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(IPStrategy)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *IPWhiteList) DeepCopyInto(out *IPWhiteList) {
|
||||||
|
*out = *in
|
||||||
|
if in.SourceRange != nil {
|
||||||
|
in, out := &in.SourceRange, &out.SourceRange
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.IPStrategy != nil {
|
||||||
|
in, out := &in.IPStrategy, &out.IPStrategy
|
||||||
|
*out = new(IPStrategy)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPWhiteList.
|
||||||
|
func (in *IPWhiteList) DeepCopy() *IPWhiteList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(IPWhiteList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *MaxConn) DeepCopyInto(out *MaxConn) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaxConn.
|
||||||
|
func (in *MaxConn) DeepCopy() *MaxConn {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(MaxConn)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Middleware) DeepCopyInto(out *Middleware) {
|
||||||
|
*out = *in
|
||||||
|
if in.AddPrefix != nil {
|
||||||
|
in, out := &in.AddPrefix, &out.AddPrefix
|
||||||
|
*out = new(AddPrefix)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.StripPrefix != nil {
|
||||||
|
in, out := &in.StripPrefix, &out.StripPrefix
|
||||||
|
*out = new(StripPrefix)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.StripPrefixRegex != nil {
|
||||||
|
in, out := &in.StripPrefixRegex, &out.StripPrefixRegex
|
||||||
|
*out = new(StripPrefixRegex)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.ReplacePath != nil {
|
||||||
|
in, out := &in.ReplacePath, &out.ReplacePath
|
||||||
|
*out = new(ReplacePath)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.ReplacePathRegex != nil {
|
||||||
|
in, out := &in.ReplacePathRegex, &out.ReplacePathRegex
|
||||||
|
*out = new(ReplacePathRegex)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.Chain != nil {
|
||||||
|
in, out := &in.Chain, &out.Chain
|
||||||
|
*out = new(Chain)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.IPWhiteList != nil {
|
||||||
|
in, out := &in.IPWhiteList, &out.IPWhiteList
|
||||||
|
*out = new(IPWhiteList)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.Headers != nil {
|
||||||
|
in, out := &in.Headers, &out.Headers
|
||||||
|
*out = new(Headers)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.Errors != nil {
|
||||||
|
in, out := &in.Errors, &out.Errors
|
||||||
|
*out = new(ErrorPage)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.RateLimit != nil {
|
||||||
|
in, out := &in.RateLimit, &out.RateLimit
|
||||||
|
*out = new(RateLimit)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.RedirectRegex != nil {
|
||||||
|
in, out := &in.RedirectRegex, &out.RedirectRegex
|
||||||
|
*out = new(RedirectRegex)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.RedirectScheme != nil {
|
||||||
|
in, out := &in.RedirectScheme, &out.RedirectScheme
|
||||||
|
*out = new(RedirectScheme)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.BasicAuth != nil {
|
||||||
|
in, out := &in.BasicAuth, &out.BasicAuth
|
||||||
|
*out = new(BasicAuth)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.DigestAuth != nil {
|
||||||
|
in, out := &in.DigestAuth, &out.DigestAuth
|
||||||
|
*out = new(DigestAuth)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.ForwardAuth != nil {
|
||||||
|
in, out := &in.ForwardAuth, &out.ForwardAuth
|
||||||
|
*out = new(ForwardAuth)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.MaxConn != nil {
|
||||||
|
in, out := &in.MaxConn, &out.MaxConn
|
||||||
|
*out = new(MaxConn)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.Buffering != nil {
|
||||||
|
in, out := &in.Buffering, &out.Buffering
|
||||||
|
*out = new(Buffering)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.CircuitBreaker != nil {
|
||||||
|
in, out := &in.CircuitBreaker, &out.CircuitBreaker
|
||||||
|
*out = new(CircuitBreaker)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.Compress != nil {
|
||||||
|
in, out := &in.Compress, &out.Compress
|
||||||
|
*out = new(Compress)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.PassTLSClientCert != nil {
|
||||||
|
in, out := &in.PassTLSClientCert, &out.PassTLSClientCert
|
||||||
|
*out = new(PassTLSClientCert)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
if in.Retry != nil {
|
||||||
|
in, out := &in.Retry, &out.Retry
|
||||||
|
*out = new(Retry)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Middleware.
|
||||||
|
func (in *Middleware) DeepCopy() *Middleware {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Middleware)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *PassTLSClientCert) DeepCopyInto(out *PassTLSClientCert) {
|
||||||
|
*out = *in
|
||||||
|
if in.Info != nil {
|
||||||
|
in, out := &in.Info, &out.Info
|
||||||
|
*out = new(TLSClientCertificateInfo)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PassTLSClientCert.
|
||||||
|
func (in *PassTLSClientCert) DeepCopy() *PassTLSClientCert {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(PassTLSClientCert)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Rate) DeepCopyInto(out *Rate) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rate.
|
||||||
|
func (in *Rate) DeepCopy() *Rate {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Rate)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RateLimit) DeepCopyInto(out *RateLimit) {
|
||||||
|
*out = *in
|
||||||
|
if in.RateSet != nil {
|
||||||
|
in, out := &in.RateSet, &out.RateSet
|
||||||
|
*out = make(map[string]*Rate, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
var outVal *Rate
|
||||||
|
if val == nil {
|
||||||
|
(*out)[key] = nil
|
||||||
|
} else {
|
||||||
|
in, out := &val, &outVal
|
||||||
|
*out = new(Rate)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
(*out)[key] = outVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RateLimit.
|
||||||
|
func (in *RateLimit) DeepCopy() *RateLimit {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RateLimit)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RedirectRegex) DeepCopyInto(out *RedirectRegex) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedirectRegex.
|
||||||
|
func (in *RedirectRegex) DeepCopy() *RedirectRegex {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RedirectRegex)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *RedirectScheme) DeepCopyInto(out *RedirectScheme) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedirectScheme.
|
||||||
|
func (in *RedirectScheme) DeepCopy() *RedirectScheme {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(RedirectScheme)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ReplacePath) DeepCopyInto(out *ReplacePath) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplacePath.
|
||||||
|
func (in *ReplacePath) DeepCopy() *ReplacePath {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ReplacePath)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ReplacePathRegex) DeepCopyInto(out *ReplacePathRegex) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplacePathRegex.
|
||||||
|
func (in *ReplacePathRegex) DeepCopy() *ReplacePathRegex {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ReplacePathRegex)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Retry) DeepCopyInto(out *Retry) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Retry.
|
||||||
|
func (in *Retry) DeepCopy() *Retry {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Retry)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *StripPrefix) DeepCopyInto(out *StripPrefix) {
|
||||||
|
*out = *in
|
||||||
|
if in.Prefixes != nil {
|
||||||
|
in, out := &in.Prefixes, &out.Prefixes
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StripPrefix.
|
||||||
|
func (in *StripPrefix) DeepCopy() *StripPrefix {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(StripPrefix)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *StripPrefixRegex) DeepCopyInto(out *StripPrefixRegex) {
|
||||||
|
*out = *in
|
||||||
|
if in.Regex != nil {
|
||||||
|
in, out := &in.Regex, &out.Regex
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StripPrefixRegex.
|
||||||
|
func (in *StripPrefixRegex) DeepCopy() *StripPrefixRegex {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(StripPrefixRegex)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *TLSCLientCertificateDNInfo) DeepCopyInto(out *TLSCLientCertificateDNInfo) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSCLientCertificateDNInfo.
|
||||||
|
func (in *TLSCLientCertificateDNInfo) DeepCopy() *TLSCLientCertificateDNInfo {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(TLSCLientCertificateDNInfo)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *TLSClientCertificateInfo) DeepCopyInto(out *TLSClientCertificateInfo) {
|
||||||
|
*out = *in
|
||||||
|
if in.Subject != nil {
|
||||||
|
in, out := &in.Subject, &out.Subject
|
||||||
|
*out = new(TLSCLientCertificateDNInfo)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
if in.Issuer != nil {
|
||||||
|
in, out := &in.Issuer, &out.Issuer
|
||||||
|
*out = new(TLSCLientCertificateDNInfo)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSClientCertificateInfo.
|
||||||
|
func (in *TLSClientCertificateInfo) DeepCopy() *TLSClientCertificateInfo {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(TLSClientCertificateInfo)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in Users) DeepCopyInto(out *Users) {
|
||||||
|
{
|
||||||
|
in := &in
|
||||||
|
*out = make(Users, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Users.
|
||||||
|
func (in Users) DeepCopy() Users {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Users)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return *out
|
||||||
|
}
|
28
integration/fixtures/k8s/01-crd.yml
Normal file
28
integration/fixtures/k8s/01-crd.yml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: ingressroutes.traefik.containo.us
|
||||||
|
|
||||||
|
spec:
|
||||||
|
group: traefik.containo.us
|
||||||
|
version: v1alpha1
|
||||||
|
names:
|
||||||
|
kind: IngressRoute
|
||||||
|
plural: ingressroutes
|
||||||
|
singular: ingressroute
|
||||||
|
scope: Namespaced
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: middlewares.traefik.containo.us
|
||||||
|
|
||||||
|
spec:
|
||||||
|
group: traefik.containo.us
|
||||||
|
version: v1alpha1
|
||||||
|
names:
|
||||||
|
kind: Middleware
|
||||||
|
plural: middlewares
|
||||||
|
singular: middleware
|
||||||
|
scope: Namespaced
|
|
@ -1,11 +1,12 @@
|
||||||
---
|
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
metadata:
|
metadata:
|
||||||
name: whoami
|
name: whoami
|
||||||
|
namespace: default
|
||||||
labels:
|
labels:
|
||||||
app: containous
|
app: containous
|
||||||
name: whoami
|
name: whoami
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
replicas: 2
|
replicas: 2
|
||||||
selector:
|
selector:
|
||||||
|
@ -23,11 +24,14 @@ spec:
|
||||||
image: containous/whoami
|
image: containous/whoami
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: whoami
|
name: whoami
|
||||||
|
namespace: default
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
|
@ -35,17 +39,3 @@ spec:
|
||||||
selector:
|
selector:
|
||||||
app: containous
|
app: containous
|
||||||
task: whoami
|
task: whoami
|
||||||
---
|
|
||||||
apiVersion: extensions/v1beta1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: cheeses
|
|
||||||
spec:
|
|
||||||
rules:
|
|
||||||
- host: whoami.test
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /whoami
|
|
||||||
backend:
|
|
||||||
serviceName: whoami
|
|
||||||
servicePort: http
|
|
15
integration/fixtures/k8s/03-ingress.yml
Normal file
15
integration/fixtures/k8s/03-ingress.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: test.ingress
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: whoami.test
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /whoami
|
||||||
|
backend:
|
||||||
|
serviceName: whoami
|
||||||
|
servicePort: http
|
18
integration/fixtures/k8s/03-ingressroute.yml
Normal file
18
integration/fixtures/k8s/03-ingressroute.yml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entrypoints:
|
||||||
|
- web
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/bar`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
||||||
|
|
29
integration/fixtures/k8s/04-ingressroute.yml
Normal file
29
integration/fixtures/k8s/04-ingressroute.yml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: stripprefix
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
stripprefix:
|
||||||
|
prefixes:
|
||||||
|
- /tobestripped
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test2.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entrypoints:
|
||||||
|
- web
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/tobestripped`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
||||||
|
middlewares:
|
||||||
|
- name: stripprefix
|
11
integration/fixtures/k8s_crd.toml
Normal file
11
integration/fixtures/k8s_crd.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[global]
|
||||||
|
debug=true
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.web]
|
||||||
|
address = ":8000"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
|
||||||
|
[Providers]
|
||||||
|
[Providers.KubernetesCRD]
|
|
@ -2,7 +2,7 @@
|
||||||
debug=true
|
debug=true
|
||||||
|
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.http]
|
[entryPoints.web]
|
||||||
address = ":8000"
|
address = ":8000"
|
||||||
|
|
||||||
[api]
|
[api]
|
||||||
|
|
|
@ -73,7 +73,7 @@ func init() {
|
||||||
}
|
}
|
||||||
if *host {
|
if *host {
|
||||||
// tests launched from the host
|
// tests launched from the host
|
||||||
// check.Suite(&K8sSuite{})
|
check.Suite(&K8sSuite{})
|
||||||
check.Suite(&ProxyProtocolSuite{})
|
check.Suite(&ProxyProtocolSuite{})
|
||||||
check.Suite(&TCPSuite{})
|
check.Suite(&TCPSuite{})
|
||||||
// FIXME Provider tests
|
// FIXME Provider tests
|
||||||
|
|
|
@ -1,81 +1,27 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containous/traefik/integration/try"
|
"github.com/containous/traefik/integration/try"
|
||||||
"github.com/containous/traefik/log"
|
|
||||||
"github.com/containous/traefik/testhelpers"
|
|
||||||
"github.com/go-check/check"
|
"github.com/go-check/check"
|
||||||
checker "github.com/vdemeester/shakers"
|
checker "github.com/vdemeester/shakers"
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
v1beta12 "k8s.io/api/extensions/v1beta1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// K8sSuite
|
// K8sSuite
|
||||||
type K8sSuite struct{ BaseSuite }
|
type K8sSuite struct{ BaseSuite }
|
||||||
|
|
||||||
const (
|
|
||||||
kubeServer = "https://127.0.0.1:6443"
|
|
||||||
namespace = "default"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *K8sSuite) SetUpSuite(c *check.C) {
|
func (s *K8sSuite) SetUpSuite(c *check.C) {
|
||||||
s.createComposeProject(c, "k8s")
|
s.createComposeProject(c, "k8s")
|
||||||
s.composeProject.Start(c)
|
s.composeProject.Start(c)
|
||||||
}
|
|
||||||
|
|
||||||
func (s *K8sSuite) TearDownSuite(c *check.C) {
|
abs, err := filepath.Abs("./fixtures/k8s/kubeconfig.yaml")
|
||||||
s.composeProject.Stop(c)
|
|
||||||
os.Remove("./resources/compose/output/kubeconfig.yaml")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseK8sYaml(fileR []byte) []runtime.Object {
|
|
||||||
acceptedK8sTypes := regexp.MustCompile(`(Deployment|Service|Ingress)`)
|
|
||||||
sepYamlfiles := strings.Split(string(fileR), "---")
|
|
||||||
retVal := make([]runtime.Object, 0, len(sepYamlfiles))
|
|
||||||
for _, f := range sepYamlfiles {
|
|
||||||
if f == "\n" || f == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
decode := scheme.Codecs.UniversalDeserializer().Decode
|
|
||||||
obj, groupVersionKind, err := decode([]byte(f), nil, nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.WithoutContext().Debugf("Error while decoding YAML object. Err was: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !acceptedK8sTypes.MatchString(groupVersionKind.Kind) {
|
|
||||||
log.WithoutContext().Debugf("The custom-roles configMap contained K8s object types which are not supported! Skipping object with type: %s", groupVersionKind.Kind)
|
|
||||||
} else {
|
|
||||||
retVal = append(retVal, obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retVal
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *K8sSuite) TestSimpleDefaultConfig(c *check.C) {
|
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, kubeServer, nil)
|
|
||||||
err := try.RequestWithTransport(req, time.Second*60, &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}, try.StatusCodeIs(http.StatusUnauthorized))
|
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
abs, err := filepath.Abs("./resources/compose/output/kubeconfig.yaml")
|
err = try.Do(60*time.Second, try.DoCondition(func() error {
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
err = try.Do(time.Second*60, try.DoCondition(func() error {
|
|
||||||
_, err := os.Stat(abs)
|
_, err := os.Stat(abs)
|
||||||
return err
|
return err
|
||||||
}))
|
}))
|
||||||
|
@ -83,41 +29,54 @@ func (s *K8sSuite) TestSimpleDefaultConfig(c *check.C) {
|
||||||
|
|
||||||
err = os.Setenv("KUBECONFIG", abs)
|
err = os.Setenv("KUBECONFIG", abs)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *K8sSuite) TearDownSuite(c *check.C) {
|
||||||
|
s.composeProject.Stop(c)
|
||||||
|
|
||||||
|
err := os.Remove("./fixtures/k8s/kubeconfig.yaml")
|
||||||
|
if err != nil {
|
||||||
|
c.Log(err)
|
||||||
|
}
|
||||||
|
err = os.Remove("./fixtures/k8s/coredns.yaml")
|
||||||
|
if err != nil {
|
||||||
|
c.Log(err)
|
||||||
|
}
|
||||||
|
err = os.Remove("./fixtures/k8s/traefik.yaml")
|
||||||
|
if err != nil {
|
||||||
|
c.Log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *K8sSuite) TestIngressSimple(c *check.C) {
|
||||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_default.toml"))
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_default.toml"))
|
||||||
defer display(c)
|
defer display(c)
|
||||||
|
|
||||||
err = cmd.Start()
|
err := cmd.Start()
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer cmd.Process.Kill()
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
config, err := clientcmd.BuildConfigFromFlags("", abs)
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Host(`whoami.test`)"))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
|
}
|
||||||
clientset, err := kubernetes.NewForConfig(config)
|
|
||||||
c.Assert(err, checker.IsNil)
|
func (s *K8sSuite) TestCRDSimple(c *check.C) {
|
||||||
|
cmd, display := s.traefikCmd(withConfigFile("fixtures/k8s_crd.toml"))
|
||||||
yamlContent, err := ioutil.ReadFile("./fixtures/k8s/test.yml")
|
defer display(c)
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
err := cmd.Start()
|
||||||
k8sObjects := parseK8sYaml(yamlContent)
|
c.Assert(err, checker.IsNil)
|
||||||
for _, obj := range k8sObjects {
|
defer cmd.Process.Kill()
|
||||||
switch o := obj.(type) {
|
|
||||||
case *v1beta12.Deployment:
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Host(`foo.com`)"))
|
||||||
_, err := clientset.ExtensionsV1beta1().Deployments(namespace).Create(o)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
case *v1.Service:
|
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("PathPrefix(`/tobestripped`)"))
|
||||||
_, err := clientset.CoreV1().Services(namespace).Create(o)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
case *v1beta12.Ingress:
|
err = try.GetRequest("http://127.0.0.1:8080/api/providers/kubernetescrd/routers", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("default/stripprefix"))
|
||||||
_, err := clientset.ExtensionsV1beta1().Ingresses(namespace).Create(o)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
default:
|
err = try.GetRequest("http://127.0.0.1:8080/api/providers/kubernetescrd/middlewares", 1*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("stripprefix"))
|
||||||
log.WithoutContext().Errorf("Unknown runtime object %+v %T", o, o)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers/kubernetes/routers", 60*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("Host(`whoami.test`)"))
|
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
}
|
}
|
||||||
|
|
1
integration/resources/compose/.gitignore
vendored
Normal file
1
integration/resources/compose/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
output/
|
|
@ -1,17 +1,18 @@
|
||||||
server:
|
server:
|
||||||
image: rancher/k3s:v0.2.0-rc4
|
image: rancher/k3s:v0.2.0
|
||||||
command: server --disable-agent
|
command: server --disable-agent --no-deploy traefik
|
||||||
environment:
|
environment:
|
||||||
- K3S_CLUSTER_SECRET=somethingtotallyrandom
|
- K3S_CLUSTER_SECRET=somethingtotallyrandom
|
||||||
- K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
|
- K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
|
||||||
- K3S_KUBECONFIG_MODE=666
|
- K3S_KUBECONFIG_MODE=666
|
||||||
volumes:
|
volumes:
|
||||||
- ./output:/output
|
- ../../fixtures/k8s:/output
|
||||||
|
- ../../fixtures/k8s:/var/lib/rancher/k3s/server/manifests
|
||||||
ports:
|
ports:
|
||||||
- 6443:6443
|
- 6443:6443
|
||||||
|
|
||||||
node:
|
node:
|
||||||
image: rancher/k3s:v0.2.0-rc4
|
image: rancher/k3s:v0.2.0
|
||||||
privileged: true
|
privileged: true
|
||||||
links:
|
links:
|
||||||
- server
|
- server
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"github.com/containous/traefik/old/provider/ecs"
|
"github.com/containous/traefik/old/provider/ecs"
|
||||||
"github.com/containous/traefik/old/provider/etcd"
|
"github.com/containous/traefik/old/provider/etcd"
|
||||||
"github.com/containous/traefik/old/provider/eureka"
|
"github.com/containous/traefik/old/provider/eureka"
|
||||||
"github.com/containous/traefik/old/provider/kubernetes"
|
|
||||||
"github.com/containous/traefik/old/provider/mesos"
|
"github.com/containous/traefik/old/provider/mesos"
|
||||||
"github.com/containous/traefik/old/provider/rancher"
|
"github.com/containous/traefik/old/provider/rancher"
|
||||||
"github.com/containous/traefik/old/provider/rest"
|
"github.com/containous/traefik/old/provider/rest"
|
||||||
|
@ -31,6 +30,7 @@ import (
|
||||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||||
"github.com/containous/traefik/provider/docker"
|
"github.com/containous/traefik/provider/docker"
|
||||||
"github.com/containous/traefik/provider/file"
|
"github.com/containous/traefik/provider/file"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/ingress"
|
||||||
newtypes "github.com/containous/traefik/types"
|
newtypes "github.com/containous/traefik/types"
|
||||||
"github.com/go-acme/lego/challenge/dns01"
|
"github.com/go-acme/lego/challenge/dns01"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -92,7 +92,7 @@ type GlobalConfiguration struct {
|
||||||
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
|
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
|
||||||
Zookeeper *zk.Provider `description:"Enable Zookeeper backend with default settings" export:"true"`
|
Zookeeper *zk.Provider `description:"Enable Zookeeper backend with default settings" export:"true"`
|
||||||
Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
|
Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
|
||||||
Kubernetes *kubernetes.Provider `description:"Enable Kubernetes backend with default settings" export:"true"`
|
Kubernetes *ingress.Provider `description:"Enable Kubernetes backend with default settings" export:"true"`
|
||||||
Mesos *mesos.Provider `description:"Enable Mesos backend with default settings" export:"true"`
|
Mesos *mesos.Provider `description:"Enable Mesos backend with default settings" export:"true"`
|
||||||
Eureka *eureka.Provider `description:"Enable Eureka backend with default settings" export:"true"`
|
Eureka *eureka.Provider `description:"Enable Eureka backend with default settings" export:"true"`
|
||||||
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
|
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/old/provider/label"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
|
|
||||||
annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm"
|
|
||||||
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
|
|
||||||
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
|
|
||||||
annotationKubernetesAuthHeaderField = "ingress.kubernetes.io/auth-header-field"
|
|
||||||
annotationKubernetesAuthForwardResponseHeaders = "ingress.kubernetes.io/auth-response-headers"
|
|
||||||
annotationKubernetesAuthRemoveHeader = "ingress.kubernetes.io/auth-remove-header"
|
|
||||||
annotationKubernetesAuthForwardURL = "ingress.kubernetes.io/auth-url"
|
|
||||||
annotationKubernetesAuthForwardTrustHeaders = "ingress.kubernetes.io/auth-trust-headers"
|
|
||||||
annotationKubernetesAuthForwardTLSSecret = "ingress.kubernetes.io/auth-tls-secret"
|
|
||||||
annotationKubernetesAuthForwardTLSInsecure = "ingress.kubernetes.io/auth-tls-insecure"
|
|
||||||
annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target"
|
|
||||||
annotationKubernetesWhiteListSourceRange = "ingress.kubernetes.io/whitelist-source-range"
|
|
||||||
annotationKubernetesWhiteListIPStrategy = "ingress.kubernetes.io/whitelist-ipstrategy"
|
|
||||||
annotationKubernetesWhiteListIPStrategyDepth = "ingress.kubernetes.io/whitelist-ipstrategy-depth"
|
|
||||||
annotationKubernetesWhiteListIPStrategyExcludedIPs = "ingress.kubernetes.io/whitelist-ipstrategy-excluded-ips"
|
|
||||||
annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host"
|
|
||||||
annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert" // Deprecated
|
|
||||||
annotationKubernetesPassTLSClientCert = "ingress.kubernetes.io/pass-client-tls-cert"
|
|
||||||
annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points"
|
|
||||||
annotationKubernetesPriority = "ingress.kubernetes.io/priority"
|
|
||||||
annotationKubernetesCircuitBreakerExpression = "ingress.kubernetes.io/circuit-breaker-expression"
|
|
||||||
annotationKubernetesLoadBalancerMethod = "ingress.kubernetes.io/load-balancer-method"
|
|
||||||
annotationKubernetesAffinity = "ingress.kubernetes.io/affinity"
|
|
||||||
annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name"
|
|
||||||
annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type"
|
|
||||||
annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point"
|
|
||||||
annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent"
|
|
||||||
annotationKubernetesRedirectRegex = "ingress.kubernetes.io/redirect-regex"
|
|
||||||
annotationKubernetesRedirectReplacement = "ingress.kubernetes.io/redirect-replacement"
|
|
||||||
annotationKubernetesMaxConnAmount = "ingress.kubernetes.io/max-conn-amount"
|
|
||||||
annotationKubernetesMaxConnExtractorFunc = "ingress.kubernetes.io/max-conn-extractor-func"
|
|
||||||
annotationKubernetesRateLimit = "ingress.kubernetes.io/rate-limit"
|
|
||||||
annotationKubernetesErrorPages = "ingress.kubernetes.io/error-pages"
|
|
||||||
annotationKubernetesBuffering = "ingress.kubernetes.io/buffering"
|
|
||||||
annotationKubernetesResponseForwardingFlushInterval = "ingress.kubernetes.io/responseforwarding-flushinterval"
|
|
||||||
annotationKubernetesAppRoot = "ingress.kubernetes.io/app-root"
|
|
||||||
annotationKubernetesServiceWeights = "ingress.kubernetes.io/service-weights"
|
|
||||||
annotationKubernetesRequestModifier = "ingress.kubernetes.io/request-modifier"
|
|
||||||
|
|
||||||
annotationKubernetesSSLForceHost = "ingress.kubernetes.io/ssl-force-host"
|
|
||||||
annotationKubernetesSSLRedirect = "ingress.kubernetes.io/ssl-redirect"
|
|
||||||
annotationKubernetesHSTSMaxAge = "ingress.kubernetes.io/hsts-max-age"
|
|
||||||
annotationKubernetesHSTSIncludeSubdomains = "ingress.kubernetes.io/hsts-include-subdomains"
|
|
||||||
annotationKubernetesCustomRequestHeaders = "ingress.kubernetes.io/custom-request-headers"
|
|
||||||
annotationKubernetesCustomResponseHeaders = "ingress.kubernetes.io/custom-response-headers"
|
|
||||||
annotationKubernetesAllowedHosts = "ingress.kubernetes.io/allowed-hosts"
|
|
||||||
annotationKubernetesProxyHeaders = "ingress.kubernetes.io/proxy-headers"
|
|
||||||
annotationKubernetesSSLTemporaryRedirect = "ingress.kubernetes.io/ssl-temporary-redirect"
|
|
||||||
annotationKubernetesSSLHost = "ingress.kubernetes.io/ssl-host"
|
|
||||||
annotationKubernetesSSLProxyHeaders = "ingress.kubernetes.io/ssl-proxy-headers"
|
|
||||||
annotationKubernetesHSTSPreload = "ingress.kubernetes.io/hsts-preload"
|
|
||||||
annotationKubernetesForceHSTSHeader = "ingress.kubernetes.io/force-hsts"
|
|
||||||
annotationKubernetesFrameDeny = "ingress.kubernetes.io/frame-deny"
|
|
||||||
annotationKubernetesCustomFrameOptionsValue = "ingress.kubernetes.io/custom-frame-options-value"
|
|
||||||
annotationKubernetesContentTypeNosniff = "ingress.kubernetes.io/content-type-nosniff"
|
|
||||||
annotationKubernetesBrowserXSSFilter = "ingress.kubernetes.io/browser-xss-filter"
|
|
||||||
annotationKubernetesCustomBrowserXSSValue = "ingress.kubernetes.io/custom-browser-xss-value"
|
|
||||||
annotationKubernetesContentSecurityPolicy = "ingress.kubernetes.io/content-security-policy"
|
|
||||||
annotationKubernetesPublicKey = "ingress.kubernetes.io/public-key"
|
|
||||||
annotationKubernetesReferrerPolicy = "ingress.kubernetes.io/referrer-policy"
|
|
||||||
annotationKubernetesIsDevelopment = "ingress.kubernetes.io/is-development"
|
|
||||||
annotationKubernetesProtocol = "ingress.kubernetes.io/protocol"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getAnnotationName(annotations map[string]string, name string) string {
|
|
||||||
if _, ok := annotations[name]; ok {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := annotations[label.Prefix+name]; ok {
|
|
||||||
return label.Prefix + name
|
|
||||||
}
|
|
||||||
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStringValue(annotations map[string]string, annotation string, defaultValue string) string {
|
|
||||||
annotationName := getAnnotationName(annotations, annotation)
|
|
||||||
return label.GetStringValue(annotations, annotationName, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStringSafeValue(annotations map[string]string, annotation string, defaultValue string) (string, error) {
|
|
||||||
annotationName := getAnnotationName(annotations, annotation)
|
|
||||||
value := label.GetStringValue(annotations, annotationName, defaultValue)
|
|
||||||
_, err := strconv.Unquote(`"` + value + `"`)
|
|
||||||
return value, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBoolValue(annotations map[string]string, annotation string, defaultValue bool) bool {
|
|
||||||
annotationName := getAnnotationName(annotations, annotation)
|
|
||||||
return label.GetBoolValue(annotations, annotationName, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIntValue(annotations map[string]string, annotation string, defaultValue int) int {
|
|
||||||
annotationName := getAnnotationName(annotations, annotation)
|
|
||||||
return label.GetIntValue(annotations, annotationName, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getInt64Value(annotations map[string]string, annotation string, defaultValue int64) int64 {
|
|
||||||
annotationName := getAnnotationName(annotations, annotation)
|
|
||||||
return label.GetInt64Value(annotations, annotationName, defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSliceStringValue(annotations map[string]string, annotation string) []string {
|
|
||||||
annotationName := getAnnotationName(annotations, annotation)
|
|
||||||
return label.GetSliceStringValue(annotations, annotationName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMapValue(annotations map[string]string, annotation string) map[string]string {
|
|
||||||
annotationName := getAnnotationName(annotations, annotation)
|
|
||||||
return label.GetMapValue(annotations, annotationName)
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/old/provider/label"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetAnnotationName(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
annotations map[string]string
|
|
||||||
name string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "with standard annotation",
|
|
||||||
name: annotationKubernetesPreserveHost,
|
|
||||||
annotations: map[string]string{
|
|
||||||
annotationKubernetesPreserveHost: "true",
|
|
||||||
},
|
|
||||||
expected: annotationKubernetesPreserveHost,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "with prefixed annotation",
|
|
||||||
name: annotationKubernetesPreserveHost,
|
|
||||||
annotations: map[string]string{
|
|
||||||
label.Prefix + annotationKubernetesPreserveHost: "true",
|
|
||||||
},
|
|
||||||
expected: label.Prefix + annotationKubernetesPreserveHost,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
actual := getAnnotationName(test.annotations, test.name)
|
|
||||||
assert.Equal(t, test.expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,647 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containous/flaeg/parse"
|
|
||||||
"github.com/containous/traefik/old/provider/label"
|
|
||||||
"github.com/containous/traefik/old/types"
|
|
||||||
"github.com/containous/traefik/tls"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildConfiguration(opts ...func(*types.Configuration)) *types.Configuration {
|
|
||||||
conf := &types.Configuration{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(conf)
|
|
||||||
}
|
|
||||||
return conf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backend
|
|
||||||
|
|
||||||
func backends(opts ...func(*types.Backend) string) func(*types.Configuration) {
|
|
||||||
return func(c *types.Configuration) {
|
|
||||||
c.Backends = make(map[string]*types.Backend)
|
|
||||||
for _, opt := range opts {
|
|
||||||
b := &types.Backend{}
|
|
||||||
name := opt(b)
|
|
||||||
c.Backends[name] = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func backend(name string, opts ...func(*types.Backend)) func(*types.Backend) string {
|
|
||||||
return func(b *types.Backend) string {
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(b)
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func servers(opts ...func(*types.Server) string) func(*types.Backend) {
|
|
||||||
return func(b *types.Backend) {
|
|
||||||
b.Servers = make(map[string]types.Server)
|
|
||||||
for _, opt := range opts {
|
|
||||||
s := &types.Server{}
|
|
||||||
name := opt(s)
|
|
||||||
b.Servers[name] = *s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func server(url string, opts ...func(*types.Server)) func(*types.Server) string {
|
|
||||||
return func(s *types.Server) string {
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
s.URL = url
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func weight(value int) func(*types.Server) {
|
|
||||||
return func(s *types.Server) {
|
|
||||||
s.Weight = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lbMethod(method string) func(*types.Backend) {
|
|
||||||
return func(b *types.Backend) {
|
|
||||||
if b.LoadBalancer == nil {
|
|
||||||
b.LoadBalancer = &types.LoadBalancer{}
|
|
||||||
}
|
|
||||||
b.LoadBalancer.Method = method
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func lbStickiness() func(*types.Backend) {
|
|
||||||
return func(b *types.Backend) {
|
|
||||||
if b.LoadBalancer == nil {
|
|
||||||
b.LoadBalancer = &types.LoadBalancer{}
|
|
||||||
}
|
|
||||||
b.LoadBalancer.Stickiness = &types.Stickiness{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func circuitBreaker(exp string) func(*types.Backend) {
|
|
||||||
return func(b *types.Backend) {
|
|
||||||
b.CircuitBreaker = &types.CircuitBreaker{}
|
|
||||||
b.CircuitBreaker.Expression = exp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func responseForwarding(interval string) func(*types.Backend) {
|
|
||||||
return func(b *types.Backend) {
|
|
||||||
b.ResponseForwarding = &types.ResponseForwarding{}
|
|
||||||
b.ResponseForwarding.FlushInterval = interval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buffering(opts ...func(*types.Buffering)) func(*types.Backend) {
|
|
||||||
return func(b *types.Backend) {
|
|
||||||
if b.Buffering == nil {
|
|
||||||
b.Buffering = &types.Buffering{}
|
|
||||||
}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(b.Buffering)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func maxRequestBodyBytes(value int64) func(*types.Buffering) {
|
|
||||||
return func(b *types.Buffering) {
|
|
||||||
b.MaxRequestBodyBytes = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func memRequestBodyBytes(value int64) func(*types.Buffering) {
|
|
||||||
return func(b *types.Buffering) {
|
|
||||||
b.MemRequestBodyBytes = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func maxResponseBodyBytes(value int64) func(*types.Buffering) {
|
|
||||||
return func(b *types.Buffering) {
|
|
||||||
b.MaxResponseBodyBytes = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func memResponseBodyBytes(value int64) func(*types.Buffering) {
|
|
||||||
return func(b *types.Buffering) {
|
|
||||||
b.MemResponseBodyBytes = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func retrying(exp string) func(*types.Buffering) {
|
|
||||||
return func(b *types.Buffering) {
|
|
||||||
b.RetryExpression = exp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func maxConnExtractorFunc(exp string) func(*types.Backend) {
|
|
||||||
return func(b *types.Backend) {
|
|
||||||
if b.MaxConn == nil {
|
|
||||||
b.MaxConn = &types.MaxConn{}
|
|
||||||
}
|
|
||||||
b.MaxConn.ExtractorFunc = exp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func maxConnAmount(value int64) func(*types.Backend) {
|
|
||||||
return func(b *types.Backend) {
|
|
||||||
if b.MaxConn == nil {
|
|
||||||
b.MaxConn = &types.MaxConn{}
|
|
||||||
}
|
|
||||||
b.MaxConn.Amount = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frontend
|
|
||||||
|
|
||||||
func buildFrontends(opts ...func(*types.Frontend) string) map[string]*types.Frontend {
|
|
||||||
fronts := make(map[string]*types.Frontend)
|
|
||||||
for _, opt := range opts {
|
|
||||||
f := &types.Frontend{}
|
|
||||||
name := opt(f)
|
|
||||||
fronts[name] = f
|
|
||||||
}
|
|
||||||
return fronts
|
|
||||||
}
|
|
||||||
|
|
||||||
func frontends(opts ...func(*types.Frontend) string) func(*types.Configuration) {
|
|
||||||
return func(c *types.Configuration) {
|
|
||||||
c.Frontends = make(map[string]*types.Frontend)
|
|
||||||
for _, opt := range opts {
|
|
||||||
f := &types.Frontend{}
|
|
||||||
name := opt(f)
|
|
||||||
c.Frontends[name] = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func frontend(backend string, opts ...func(*types.Frontend)) func(*types.Frontend) string {
|
|
||||||
return func(f *types.Frontend) string {
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(f)
|
|
||||||
}
|
|
||||||
// related the function frontendName
|
|
||||||
name := f.Backend
|
|
||||||
f.Backend = backend
|
|
||||||
if len(name) > 0 {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
return backend
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func frontendName(name string) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
// store temporary the frontend name into the backend name
|
|
||||||
f.Backend = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func passHostHeader() func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
f.PassHostHeader = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func entryPoints(eps ...string) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
f.EntryPoints = eps
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func basicAuthDeprecated(auth ...string) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
f.Auth = &types.Auth{Basic: &types.Basic{Users: auth}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func auth(opt func(*types.Auth)) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
auth := &types.Auth{}
|
|
||||||
opt(auth)
|
|
||||||
f.Auth = auth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func basicAuth(users ...string) func(*types.Auth) {
|
|
||||||
return func(a *types.Auth) {
|
|
||||||
a.Basic = &types.Basic{Users: users}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func forwardAuth(forwardURL string, opts ...func(*types.Forward)) func(*types.Auth) {
|
|
||||||
return func(a *types.Auth) {
|
|
||||||
fwd := &types.Forward{Address: forwardURL}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(fwd)
|
|
||||||
}
|
|
||||||
a.Forward = fwd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fwdAuthResponseHeaders(headers ...string) func(*types.Forward) {
|
|
||||||
return func(f *types.Forward) {
|
|
||||||
f.AuthResponseHeaders = headers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fwdTrustForwardHeader() func(*types.Forward) {
|
|
||||||
return func(f *types.Forward) {
|
|
||||||
f.TrustForwardHeader = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fwdAuthTLS(cert, key string, insecure bool) func(*types.Forward) {
|
|
||||||
return func(f *types.Forward) {
|
|
||||||
f.TLS = &types.ClientTLS{Cert: cert, Key: key, InsecureSkipVerify: insecure}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func whiteListRange(ranges ...string) func(*types.WhiteList) {
|
|
||||||
return func(wl *types.WhiteList) {
|
|
||||||
wl.SourceRange = ranges
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func whiteListIPStrategy(depth int, excludedIPs ...string) func(*types.WhiteList) {
|
|
||||||
return func(wl *types.WhiteList) {
|
|
||||||
wl.IPStrategy = &types.IPStrategy{
|
|
||||||
Depth: depth,
|
|
||||||
ExcludedIPs: excludedIPs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func whiteList(opts ...func(*types.WhiteList)) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
if f.WhiteList == nil {
|
|
||||||
f.WhiteList = &types.WhiteList{}
|
|
||||||
}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(f.WhiteList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func priority(value int) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
f.Priority = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func headers(h *types.Headers) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
f.Headers = h
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func redirectEntryPoint(name string) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
if f.Redirect == nil {
|
|
||||||
f.Redirect = &types.Redirect{}
|
|
||||||
}
|
|
||||||
f.Redirect.EntryPoint = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func redirectRegex(regex, replacement string) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
if f.Redirect == nil {
|
|
||||||
f.Redirect = &types.Redirect{}
|
|
||||||
}
|
|
||||||
f.Redirect.Regex = regex
|
|
||||||
f.Redirect.Replacement = replacement
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorPage(name string, opts ...func(*types.ErrorPage)) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
if f.Errors == nil {
|
|
||||||
f.Errors = make(map[string]*types.ErrorPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(name) > 0 {
|
|
||||||
f.Errors[name] = &types.ErrorPage{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(f.Errors[name])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorStatus(status ...string) func(*types.ErrorPage) {
|
|
||||||
return func(page *types.ErrorPage) {
|
|
||||||
page.Status = status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorQuery(query string) func(*types.ErrorPage) {
|
|
||||||
return func(page *types.ErrorPage) {
|
|
||||||
page.Query = query
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorBackend(backend string) func(*types.ErrorPage) {
|
|
||||||
return func(page *types.ErrorPage) {
|
|
||||||
page.Backend = backend
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func rateLimit(opts ...func(*types.RateLimit)) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
if f.RateLimit == nil {
|
|
||||||
f.RateLimit = &types.RateLimit{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(f.RateLimit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func rateExtractorFunc(exp string) func(*types.RateLimit) {
|
|
||||||
return func(limit *types.RateLimit) {
|
|
||||||
limit.ExtractorFunc = exp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func rateSet(name string, opts ...func(*types.Rate)) func(*types.RateLimit) {
|
|
||||||
return func(limit *types.RateLimit) {
|
|
||||||
if limit.RateSet == nil {
|
|
||||||
limit.RateSet = make(map[string]*types.Rate)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(name) > 0 {
|
|
||||||
limit.RateSet[name] = &types.Rate{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(limit.RateSet[name])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func limitAverage(avg int64) func(*types.Rate) {
|
|
||||||
return func(rate *types.Rate) {
|
|
||||||
rate.Average = avg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func limitBurst(burst int64) func(*types.Rate) {
|
|
||||||
return func(rate *types.Rate) {
|
|
||||||
rate.Burst = burst
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func limitPeriod(period time.Duration) func(*types.Rate) {
|
|
||||||
return func(rate *types.Rate) {
|
|
||||||
rate.Period = parse.Duration(period)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
func passTLSCert() func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
f.PassTLSCert = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func passTLSClientCert() func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
f.PassTLSClientCert = &types.TLSClientHeaders{
|
|
||||||
PEM: true,
|
|
||||||
Infos: &types.TLSClientCertificateInfos{
|
|
||||||
NotAfter: true,
|
|
||||||
NotBefore: true,
|
|
||||||
Subject: &types.TLSCLientCertificateDNInfos{
|
|
||||||
CommonName: true,
|
|
||||||
Country: true,
|
|
||||||
DomainComponent: true,
|
|
||||||
Locality: true,
|
|
||||||
Organization: true,
|
|
||||||
Province: true,
|
|
||||||
SerialNumber: true,
|
|
||||||
},
|
|
||||||
Issuer: &types.TLSCLientCertificateDNInfos{
|
|
||||||
CommonName: true,
|
|
||||||
Country: true,
|
|
||||||
DomainComponent: true,
|
|
||||||
Locality: true,
|
|
||||||
Organization: true,
|
|
||||||
Province: true,
|
|
||||||
SerialNumber: true,
|
|
||||||
},
|
|
||||||
Sans: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func routes(opts ...func(*types.Route) string) func(*types.Frontend) {
|
|
||||||
return func(f *types.Frontend) {
|
|
||||||
f.Routes = make(map[string]types.Route)
|
|
||||||
for _, opt := range opts {
|
|
||||||
s := &types.Route{}
|
|
||||||
name := opt(s)
|
|
||||||
f.Routes[name] = *s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func route(name string, rule string) func(*types.Route) string {
|
|
||||||
return func(r *types.Route) string {
|
|
||||||
r.Rule = rule
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tlsesSection(opts ...func(*tls.Configuration)) func(*types.Configuration) {
|
|
||||||
return func(c *types.Configuration) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
tlsConf := &tls.Configuration{}
|
|
||||||
opt(tlsConf)
|
|
||||||
c.TLS = append(c.TLS, tlsConf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tlsSection(opts ...func(*tls.Configuration)) func(*tls.Configuration) {
|
|
||||||
return func(c *tls.Configuration) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tlsEntryPoints(entryPoints ...string) func(*tls.Configuration) {
|
|
||||||
return func(c *tls.Configuration) {
|
|
||||||
c.Stores = entryPoints
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func certificate(cert string, key string) func(*tls.Configuration) {
|
|
||||||
return func(c *tls.Configuration) {
|
|
||||||
c.Certificate = &tls.Certificate{
|
|
||||||
CertFile: tls.FileOrContent(cert),
|
|
||||||
KeyFile: tls.FileOrContent(key),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
func TestBuildConfiguration(t *testing.T) {
|
|
||||||
actual := buildConfiguration(
|
|
||||||
backends(
|
|
||||||
backend("foo/bar",
|
|
||||||
servers(
|
|
||||||
server("http://10.10.0.1:8080", weight(1)),
|
|
||||||
server("http://10.21.0.1:8080", weight(1)),
|
|
||||||
),
|
|
||||||
lbMethod("wrr"),
|
|
||||||
),
|
|
||||||
backend("foo/namedthing",
|
|
||||||
servers(server("https://example.com", weight(1))),
|
|
||||||
lbMethod("wrr"),
|
|
||||||
),
|
|
||||||
backend("bar",
|
|
||||||
servers(
|
|
||||||
server("https://10.15.0.1:8443", weight(1)),
|
|
||||||
server("https://10.15.0.2:9443", weight(1)),
|
|
||||||
),
|
|
||||||
lbMethod("wrr"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
frontends(
|
|
||||||
frontend("foo/bar",
|
|
||||||
passHostHeader(),
|
|
||||||
routes(
|
|
||||||
route("/bar", "PathPrefix:/bar"),
|
|
||||||
route("foo", "Host:foo"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
frontend("foo/namedthing",
|
|
||||||
passHostHeader(),
|
|
||||||
routes(
|
|
||||||
route("/namedthing", "PathPrefix:/namedthing"),
|
|
||||||
route("foo", "Host:foo"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
frontend("bar",
|
|
||||||
passHostHeader(),
|
|
||||||
routes(
|
|
||||||
route("bar", "Host:bar"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
tlsesSection(
|
|
||||||
tlsSection(
|
|
||||||
tlsEntryPoints("https"),
|
|
||||||
certificate("certificate", "key"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleConfiguration(), actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleConfiguration() *types.Configuration {
|
|
||||||
return &types.Configuration{
|
|
||||||
Backends: map[string]*types.Backend{
|
|
||||||
"foo/bar": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"http://10.10.0.1:8080": {
|
|
||||||
URL: "http://10.10.0.1:8080",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"http://10.21.0.1:8080": {
|
|
||||||
URL: "http://10.21.0.1:8080",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: nil,
|
|
||||||
LoadBalancer: &types.LoadBalancer{
|
|
||||||
Method: "wrr",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"foo/namedthing": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"https://example.com": {
|
|
||||||
URL: "https://example.com",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: nil,
|
|
||||||
LoadBalancer: &types.LoadBalancer{
|
|
||||||
Method: "wrr",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"bar": {
|
|
||||||
Servers: map[string]types.Server{
|
|
||||||
"https://10.15.0.1:8443": {
|
|
||||||
URL: "https://10.15.0.1:8443",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
"https://10.15.0.2:9443": {
|
|
||||||
URL: "https://10.15.0.2:9443",
|
|
||||||
Weight: label.DefaultWeight,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
CircuitBreaker: nil,
|
|
||||||
LoadBalancer: &types.LoadBalancer{
|
|
||||||
Method: "wrr",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Frontends: map[string]*types.Frontend{
|
|
||||||
"foo/bar": {
|
|
||||||
Backend: "foo/bar",
|
|
||||||
PassHostHeader: true,
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"/bar": {
|
|
||||||
Rule: "PathPrefix:/bar",
|
|
||||||
},
|
|
||||||
"foo": {
|
|
||||||
Rule: "Host:foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"foo/namedthing": {
|
|
||||||
Backend: "foo/namedthing",
|
|
||||||
PassHostHeader: true,
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"/namedthing": {
|
|
||||||
Rule: "PathPrefix:/namedthing",
|
|
||||||
},
|
|
||||||
"foo": {
|
|
||||||
Rule: "Host:foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"bar": {
|
|
||||||
Backend: "bar",
|
|
||||||
PassHostHeader: true,
|
|
||||||
Routes: map[string]types.Route{
|
|
||||||
"bar": {
|
|
||||||
Rule: "Host:bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TLS: []*tls.Configuration{
|
|
||||||
{
|
|
||||||
Stores: []string{"https"},
|
|
||||||
Certificate: &tls.Certificate{
|
|
||||||
CertFile: tls.FileOrContent("certificate"),
|
|
||||||
KeyFile: tls.FileOrContent("key"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,158 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildEndpoint(opts ...func(*corev1.Endpoints)) *corev1.Endpoints {
|
|
||||||
e := &corev1.Endpoints{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(e)
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func eNamespace(value string) func(*corev1.Endpoints) {
|
|
||||||
return func(i *corev1.Endpoints) {
|
|
||||||
i.Namespace = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eName(value string) func(*corev1.Endpoints) {
|
|
||||||
return func(i *corev1.Endpoints) {
|
|
||||||
i.Name = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eUID(value types.UID) func(*corev1.Endpoints) {
|
|
||||||
return func(i *corev1.Endpoints) {
|
|
||||||
i.UID = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func subset(opts ...func(*corev1.EndpointSubset)) func(*corev1.Endpoints) {
|
|
||||||
return func(e *corev1.Endpoints) {
|
|
||||||
s := &corev1.EndpointSubset{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
e.Subsets = append(e.Subsets, *s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eAddresses(opts ...func(*corev1.EndpointAddress)) func(*corev1.EndpointSubset) {
|
|
||||||
return func(subset *corev1.EndpointSubset) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
a := &corev1.EndpointAddress{}
|
|
||||||
opt(a)
|
|
||||||
subset.Addresses = append(subset.Addresses, *a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eAddress(ip string) func(*corev1.EndpointAddress) {
|
|
||||||
return func(address *corev1.EndpointAddress) {
|
|
||||||
address.IP = ip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eAddressWithTargetRef(targetRef, ip string) func(*corev1.EndpointAddress) {
|
|
||||||
return func(address *corev1.EndpointAddress) {
|
|
||||||
address.TargetRef = &corev1.ObjectReference{Name: targetRef}
|
|
||||||
address.IP = ip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ePorts(opts ...func(port *corev1.EndpointPort)) func(*corev1.EndpointSubset) {
|
|
||||||
return func(spec *corev1.EndpointSubset) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
p := &corev1.EndpointPort{}
|
|
||||||
opt(p)
|
|
||||||
spec.Ports = append(spec.Ports, *p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ePort(port int32, name string) func(*corev1.EndpointPort) {
|
|
||||||
return func(sp *corev1.EndpointPort) {
|
|
||||||
sp.Port = port
|
|
||||||
sp.Name = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
func TestBuildEndpoint(t *testing.T) {
|
|
||||||
actual := buildEndpoint(
|
|
||||||
eNamespace("testing"),
|
|
||||||
eName("service3"),
|
|
||||||
eUID("3"),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.15.0.1")),
|
|
||||||
ePorts(
|
|
||||||
ePort(8080, "http"),
|
|
||||||
ePort(8443, "https"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.15.0.2")),
|
|
||||||
ePorts(
|
|
||||||
ePort(9080, "http"),
|
|
||||||
ePort(9443, "https"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleEndpoint1(), actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleEndpoint1() *corev1.Endpoints {
|
|
||||||
return &corev1.Endpoints{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "service3",
|
|
||||||
UID: "3",
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Subsets: []corev1.EndpointSubset{
|
|
||||||
{
|
|
||||||
Addresses: []corev1.EndpointAddress{
|
|
||||||
{
|
|
||||||
IP: "10.15.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Ports: []corev1.EndpointPort{
|
|
||||||
{
|
|
||||||
Name: "http",
|
|
||||||
Port: 8080,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "https",
|
|
||||||
Port: 8443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Addresses: []corev1.EndpointAddress{
|
|
||||||
{
|
|
||||||
IP: "10.15.0.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Ports: []corev1.EndpointPort{
|
|
||||||
{
|
|
||||||
Name: "http",
|
|
||||||
Port: 9080,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "https",
|
|
||||||
Port: 9443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildIngress(opts ...func(*extensionsv1beta1.Ingress)) *extensionsv1beta1.Ingress {
|
|
||||||
i := &extensionsv1beta1.Ingress{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(i)
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func iNamespace(value string) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
i.Namespace = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iAnnotation(name string, value string) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
if i.Annotations == nil {
|
|
||||||
i.Annotations = make(map[string]string)
|
|
||||||
}
|
|
||||||
i.Annotations[name] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iRules(opts ...func(*extensionsv1beta1.IngressSpec)) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
s := &extensionsv1beta1.IngressSpec{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
i.Spec = *s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iSpecBackends(opts ...func(*extensionsv1beta1.IngressSpec)) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
s := &extensionsv1beta1.IngressSpec{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
i.Spec = *s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iSpecBackend(opts ...func(*extensionsv1beta1.IngressBackend)) func(*extensionsv1beta1.IngressSpec) {
|
|
||||||
return func(s *extensionsv1beta1.IngressSpec) {
|
|
||||||
p := &extensionsv1beta1.IngressBackend{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(p)
|
|
||||||
}
|
|
||||||
s.Backend = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iIngressBackend(name string, port intstr.IntOrString) func(*extensionsv1beta1.IngressBackend) {
|
|
||||||
return func(p *extensionsv1beta1.IngressBackend) {
|
|
||||||
p.ServiceName = name
|
|
||||||
p.ServicePort = port
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iRule(opts ...func(*extensionsv1beta1.IngressRule)) func(*extensionsv1beta1.IngressSpec) {
|
|
||||||
return func(spec *extensionsv1beta1.IngressSpec) {
|
|
||||||
r := &extensionsv1beta1.IngressRule{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(r)
|
|
||||||
}
|
|
||||||
spec.Rules = append(spec.Rules, *r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iHost(name string) func(*extensionsv1beta1.IngressRule) {
|
|
||||||
return func(rule *extensionsv1beta1.IngressRule) {
|
|
||||||
rule.Host = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iPaths(opts ...func(*extensionsv1beta1.HTTPIngressRuleValue)) func(*extensionsv1beta1.IngressRule) {
|
|
||||||
return func(rule *extensionsv1beta1.IngressRule) {
|
|
||||||
rule.HTTP = &extensionsv1beta1.HTTPIngressRuleValue{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(rule.HTTP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func onePath(opts ...func(*extensionsv1beta1.HTTPIngressPath)) func(*extensionsv1beta1.HTTPIngressRuleValue) {
|
|
||||||
return func(irv *extensionsv1beta1.HTTPIngressRuleValue) {
|
|
||||||
p := &extensionsv1beta1.HTTPIngressPath{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(p)
|
|
||||||
}
|
|
||||||
irv.Paths = append(irv.Paths, *p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iPath(name string) func(*extensionsv1beta1.HTTPIngressPath) {
|
|
||||||
return func(p *extensionsv1beta1.HTTPIngressPath) {
|
|
||||||
p.Path = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iBackend(name string, port intstr.IntOrString) func(*extensionsv1beta1.HTTPIngressPath) {
|
|
||||||
return func(p *extensionsv1beta1.HTTPIngressPath) {
|
|
||||||
p.Backend = extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: name,
|
|
||||||
ServicePort: port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iTLSes(opts ...func(*extensionsv1beta1.IngressTLS)) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
iTLS := extensionsv1beta1.IngressTLS{}
|
|
||||||
opt(&iTLS)
|
|
||||||
i.Spec.TLS = append(i.Spec.TLS, iTLS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iTLS(secret string, hosts ...string) func(*extensionsv1beta1.IngressTLS) {
|
|
||||||
return func(i *extensionsv1beta1.IngressTLS) {
|
|
||||||
i.SecretName = secret
|
|
||||||
i.Hosts = hosts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
func TestBuildIngress(t *testing.T) {
|
|
||||||
i := buildIngress(
|
|
||||||
iNamespace("testing"),
|
|
||||||
iRules(
|
|
||||||
iRule(iHost("foo"), iPaths(
|
|
||||||
onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))),
|
|
||||||
onePath(iPath("/namedthing"), iBackend("service4", intstr.FromString("https")))),
|
|
||||||
),
|
|
||||||
iRule(iHost("bar"), iPaths(
|
|
||||||
onePath(iBackend("service3", intstr.FromString("https"))),
|
|
||||||
onePath(iBackend("service2", intstr.FromInt(802))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
iTLSes(
|
|
||||||
iTLS("tls-secret", "foo"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleIngress(), i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleIngress() *extensionsv1beta1.Ingress {
|
|
||||||
return &extensionsv1beta1.Ingress{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Spec: extensionsv1beta1.IngressSpec{
|
|
||||||
Rules: []extensionsv1beta1.IngressRule{
|
|
||||||
{
|
|
||||||
Host: "foo",
|
|
||||||
IngressRuleValue: extensionsv1beta1.IngressRuleValue{
|
|
||||||
HTTP: &extensionsv1beta1.HTTPIngressRuleValue{
|
|
||||||
Paths: []extensionsv1beta1.HTTPIngressPath{
|
|
||||||
{
|
|
||||||
Path: "/bar",
|
|
||||||
Backend: extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: "service1",
|
|
||||||
ServicePort: intstr.FromInt(80),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Path: "/namedthing",
|
|
||||||
Backend: extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: "service4",
|
|
||||||
ServicePort: intstr.FromString("https"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Host: "bar",
|
|
||||||
IngressRuleValue: extensionsv1beta1.IngressRuleValue{
|
|
||||||
HTTP: &extensionsv1beta1.HTTPIngressRuleValue{
|
|
||||||
Paths: []extensionsv1beta1.HTTPIngressPath{
|
|
||||||
{
|
|
||||||
Backend: extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: "service3",
|
|
||||||
ServicePort: intstr.FromString("https"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Backend: extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: "service2",
|
|
||||||
ServicePort: intstr.FromInt(802),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TLS: []extensionsv1beta1.IngressTLS{
|
|
||||||
{
|
|
||||||
Hosts: []string{"foo"},
|
|
||||||
SecretName: "tls-secret",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,232 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildService(opts ...func(*corev1.Service)) *corev1.Service {
|
|
||||||
s := &corev1.Service{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func sNamespace(value string) func(*corev1.Service) {
|
|
||||||
return func(i *corev1.Service) {
|
|
||||||
i.Namespace = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sName(value string) func(*corev1.Service) {
|
|
||||||
return func(i *corev1.Service) {
|
|
||||||
i.Name = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sUID(value types.UID) func(*corev1.Service) {
|
|
||||||
return func(i *corev1.Service) {
|
|
||||||
i.UID = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sAnnotation(name string, value string) func(*corev1.Service) {
|
|
||||||
return func(s *corev1.Service) {
|
|
||||||
if s.Annotations == nil {
|
|
||||||
s.Annotations = make(map[string]string)
|
|
||||||
}
|
|
||||||
s.Annotations[name] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sSpec(opts ...func(*corev1.ServiceSpec)) func(*corev1.Service) {
|
|
||||||
return func(s *corev1.Service) {
|
|
||||||
spec := &corev1.ServiceSpec{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(spec)
|
|
||||||
}
|
|
||||||
s.Spec = *spec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sLoadBalancerStatus(opts ...func(*corev1.LoadBalancerStatus)) func(service *corev1.Service) {
|
|
||||||
return func(s *corev1.Service) {
|
|
||||||
loadBalancer := &corev1.LoadBalancerStatus{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
if opt != nil {
|
|
||||||
opt(loadBalancer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.Status = corev1.ServiceStatus{
|
|
||||||
LoadBalancer: *loadBalancer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sLoadBalancerIngress(ip string, hostname string) func(*corev1.LoadBalancerStatus) {
|
|
||||||
return func(status *corev1.LoadBalancerStatus) {
|
|
||||||
ingress := corev1.LoadBalancerIngress{
|
|
||||||
IP: ip,
|
|
||||||
Hostname: hostname,
|
|
||||||
}
|
|
||||||
status.Ingress = append(status.Ingress, ingress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func clusterIP(ip string) func(*corev1.ServiceSpec) {
|
|
||||||
return func(spec *corev1.ServiceSpec) {
|
|
||||||
spec.ClusterIP = ip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sType(value corev1.ServiceType) func(*corev1.ServiceSpec) {
|
|
||||||
return func(spec *corev1.ServiceSpec) {
|
|
||||||
spec.Type = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sExternalName(name string) func(*corev1.ServiceSpec) {
|
|
||||||
return func(spec *corev1.ServiceSpec) {
|
|
||||||
spec.ExternalName = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sPorts(opts ...func(*corev1.ServicePort)) func(*corev1.ServiceSpec) {
|
|
||||||
return func(spec *corev1.ServiceSpec) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
p := &corev1.ServicePort{}
|
|
||||||
opt(p)
|
|
||||||
spec.Ports = append(spec.Ports, *p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sPort(port int32, name string) func(*corev1.ServicePort) {
|
|
||||||
return func(sp *corev1.ServicePort) {
|
|
||||||
sp.Port = port
|
|
||||||
sp.Name = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
func TestBuildService(t *testing.T) {
|
|
||||||
actual1 := buildService(
|
|
||||||
sName("service1"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
sUID("1"),
|
|
||||||
sSpec(
|
|
||||||
clusterIP("10.0.0.1"),
|
|
||||||
sPorts(sPort(80, "")),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleService1(), actual1)
|
|
||||||
|
|
||||||
actual2 := buildService(
|
|
||||||
sName("service2"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
sUID("2"),
|
|
||||||
sSpec(
|
|
||||||
clusterIP("10.0.0.2"),
|
|
||||||
sType("ExternalName"),
|
|
||||||
sExternalName("example.com"),
|
|
||||||
sPorts(
|
|
||||||
sPort(80, "http"),
|
|
||||||
sPort(443, "https"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleService2(), actual2)
|
|
||||||
|
|
||||||
actual3 := buildService(
|
|
||||||
sName("service3"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
sUID("3"),
|
|
||||||
sSpec(
|
|
||||||
clusterIP("10.0.0.3"),
|
|
||||||
sType("ExternalName"),
|
|
||||||
sExternalName("example.com"),
|
|
||||||
sPorts(
|
|
||||||
sPort(8080, "http"),
|
|
||||||
sPort(8443, "https"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleService3(), actual3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleService1() *corev1.Service {
|
|
||||||
return &corev1.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "service1",
|
|
||||||
UID: "1",
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Spec: corev1.ServiceSpec{
|
|
||||||
ClusterIP: "10.0.0.1",
|
|
||||||
Ports: []corev1.ServicePort{
|
|
||||||
{
|
|
||||||
Port: 80,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleService2() *corev1.Service {
|
|
||||||
return &corev1.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "service2",
|
|
||||||
UID: "2",
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Spec: corev1.ServiceSpec{
|
|
||||||
ClusterIP: "10.0.0.2",
|
|
||||||
Type: "ExternalName",
|
|
||||||
ExternalName: "example.com",
|
|
||||||
Ports: []corev1.ServicePort{
|
|
||||||
{
|
|
||||||
Name: "http",
|
|
||||||
Port: 80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "https",
|
|
||||||
Port: 443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleService3() *corev1.Service {
|
|
||||||
return &corev1.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "service3",
|
|
||||||
UID: "3",
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Spec: corev1.ServiceSpec{
|
|
||||||
ClusterIP: "10.0.0.3",
|
|
||||||
Type: "ExternalName",
|
|
||||||
ExternalName: "example.com",
|
|
||||||
Ports: []corev1.ServicePort{
|
|
||||||
{
|
|
||||||
Name: "http",
|
|
||||||
Port: 8080,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "https",
|
|
||||||
Port: 8443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
kubeerror "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTranslateNotFoundError(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
err error
|
|
||||||
expectedExists bool
|
|
||||||
expectedError error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "kubernetes not found error",
|
|
||||||
err: kubeerror.NewNotFound(schema.GroupResource{}, "foo"),
|
|
||||||
expectedExists: false,
|
|
||||||
expectedError: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "nil error",
|
|
||||||
err: nil,
|
|
||||||
expectedExists: true,
|
|
||||||
expectedError: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "not a kubernetes not found error",
|
|
||||||
err: fmt.Errorf("bar error"),
|
|
||||||
expectedExists: false,
|
|
||||||
expectedError: fmt.Errorf("bar error"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
|
||||||
test := testCase
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
exists, err := translateNotFoundError(test.err)
|
|
||||||
assert.Equal(t, test.expectedExists, exists)
|
|
||||||
assert.Equal(t, test.expectedError, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,32 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
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 *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 = val.(Namespaces)
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultPercentageValuePrecision = 3
|
|
||||||
|
|
||||||
// percentageValue is int64 form of percentage value with 10^-3 precision.
|
|
||||||
type percentageValue int64
|
|
||||||
|
|
||||||
// toFloat64 returns its decimal float64 value.
|
|
||||||
func (v percentageValue) toFloat64() float64 {
|
|
||||||
return float64(v) / (1000 * 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v percentageValue) computeWeight(count int) int {
|
|
||||||
if count == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(float64(v) / float64(count))
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns its string form of percentage value.
|
|
||||||
func (v percentageValue) String() string {
|
|
||||||
return strconv.FormatFloat(v.toFloat64()*100, 'f', defaultPercentageValuePrecision, 64) + "%"
|
|
||||||
}
|
|
||||||
|
|
||||||
// newPercentageValueFromString tries to read percentage value from string, it can be either "1.1" or "1.1%", "6%".
|
|
||||||
// It will lose the extra precision if there are more digits after decimal point.
|
|
||||||
func newPercentageValueFromString(rawValue string) (percentageValue, error) {
|
|
||||||
if strings.HasSuffix(rawValue, "%") {
|
|
||||||
rawValue = rawValue[:len(rawValue)-1]
|
|
||||||
}
|
|
||||||
value, err := strconv.ParseFloat(rawValue, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPercentageValueFromFloat64(value) / 100, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newPercentageValueFromFloat64 reads percentage value from float64
|
|
||||||
func newPercentageValueFromFloat64(f float64) percentageValue {
|
|
||||||
return percentageValue(f * (1000 * 100))
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewPercentageValueFromFloat64(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
value float64
|
|
||||||
expectedString string
|
|
||||||
expectedFloat64 float64
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
value: 0.01,
|
|
||||||
expectedString: "1.000%",
|
|
||||||
expectedFloat64: 0.01,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 0.5,
|
|
||||||
expectedString: "50.000%",
|
|
||||||
expectedFloat64: 0.5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 0.99,
|
|
||||||
expectedString: "99.000%",
|
|
||||||
expectedFloat64: 0.99,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 0.99999,
|
|
||||||
expectedString: "99.999%",
|
|
||||||
expectedFloat64: 0.99999,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: -0.99999,
|
|
||||||
expectedString: "-99.999%",
|
|
||||||
expectedFloat64: -0.99999,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: -0.9999999,
|
|
||||||
expectedString: "-99.999%",
|
|
||||||
expectedFloat64: -0.99999,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 0,
|
|
||||||
expectedString: "0.000%",
|
|
||||||
expectedFloat64: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
pvFromFloat64 := newPercentageValueFromFloat64(test.value)
|
|
||||||
|
|
||||||
assert.Equal(t, test.expectedString, pvFromFloat64.String(), "percentage string value mismatched")
|
|
||||||
assert.Equal(t, test.expectedFloat64, pvFromFloat64.toFloat64(), "percentage float64 value mismatched")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewPercentageValueFromString(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
value string
|
|
||||||
expectError bool
|
|
||||||
expectedString string
|
|
||||||
expectedFloat64 float64
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
value: "1%",
|
|
||||||
expectError: false,
|
|
||||||
expectedString: "1.000%",
|
|
||||||
expectedFloat64: 0.01,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "0.5",
|
|
||||||
expectError: false,
|
|
||||||
expectedString: "0.500%",
|
|
||||||
expectedFloat64: 0.005,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "99%",
|
|
||||||
expectError: false,
|
|
||||||
expectedString: "99.000%",
|
|
||||||
expectedFloat64: 0.99,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "99.9%",
|
|
||||||
expectError: false,
|
|
||||||
expectedString: "99.900%",
|
|
||||||
expectedFloat64: 0.999,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "-99.9%",
|
|
||||||
expectError: false,
|
|
||||||
expectedString: "-99.900%",
|
|
||||||
expectedFloat64: -0.999,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "-99.99999%",
|
|
||||||
expectError: false,
|
|
||||||
expectedString: "-99.999%",
|
|
||||||
expectedFloat64: -0.99999,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "0%",
|
|
||||||
expectError: false,
|
|
||||||
expectedString: "0.000%",
|
|
||||||
expectedFloat64: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "%",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "foo",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
pvFromString, err := newPercentageValueFromString(test.value)
|
|
||||||
|
|
||||||
if test.expectError {
|
|
||||||
require.Error(t, err, "expecting error but not happening")
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err, "fail to parse percentage value")
|
|
||||||
|
|
||||||
assert.Equal(t, test.expectedString, pvFromString.String(), "percentage string value mismatched")
|
|
||||||
assert.Equal(t, test.expectedFloat64, pvFromString.toFloat64(), "percentage float64 value mismatched")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewPercentageValue(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
stringValue string
|
|
||||||
floatValue float64
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "percentage",
|
|
||||||
stringValue: "1%",
|
|
||||||
floatValue: 0.01,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "decimal",
|
|
||||||
stringValue: "0.5",
|
|
||||||
floatValue: 0.005,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "negative percentage",
|
|
||||||
stringValue: "-99.999%",
|
|
||||||
floatValue: -0.99999,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "negative decimal",
|
|
||||||
stringValue: "-0.99999",
|
|
||||||
floatValue: -0.0099999,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "zero",
|
|
||||||
stringValue: "0%",
|
|
||||||
floatValue: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
pvFromString, err := newPercentageValueFromString(test.stringValue)
|
|
||||||
require.NoError(t, err, "fail to parse percentage value")
|
|
||||||
|
|
||||||
pvFromFloat64 := newPercentageValueFromFloat64(test.floatValue)
|
|
||||||
|
|
||||||
assert.Equal(t, pvFromString, pvFromFloat64)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,208 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/old/provider/label"
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type weightAllocator interface {
|
|
||||||
getWeight(host, path, serviceName string) int
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ weightAllocator = &defaultWeightAllocator{}
|
|
||||||
var _ weightAllocator = &fractionalWeightAllocator{}
|
|
||||||
|
|
||||||
type defaultWeightAllocator struct{}
|
|
||||||
|
|
||||||
func (d *defaultWeightAllocator) getWeight(host, path, serviceName string) int {
|
|
||||||
return label.DefaultWeight
|
|
||||||
}
|
|
||||||
|
|
||||||
type ingressService struct {
|
|
||||||
host string
|
|
||||||
path string
|
|
||||||
service string
|
|
||||||
}
|
|
||||||
|
|
||||||
type fractionalWeightAllocator map[ingressService]int
|
|
||||||
|
|
||||||
// String returns a string representation as service name / percentage tuples
|
|
||||||
// sorted by service names.
|
|
||||||
// Example: [foo-svc: 30.000% bar-svc: 70.000%]
|
|
||||||
func (f *fractionalWeightAllocator) String() string {
|
|
||||||
var sorted []ingressService
|
|
||||||
for ingServ := range map[ingressService]int(*f) {
|
|
||||||
sorted = append(sorted, ingServ)
|
|
||||||
}
|
|
||||||
sort.Slice(sorted, func(i, j int) bool {
|
|
||||||
return sorted[i].service < sorted[j].service
|
|
||||||
})
|
|
||||||
|
|
||||||
var res []string
|
|
||||||
for _, ingServ := range sorted {
|
|
||||||
res = append(res, fmt.Sprintf("%s: %s", ingServ.service, percentageValue(map[ingressService]int(*f)[ingServ])))
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("[%s]", strings.Join(res, " "))
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFractionalWeightAllocator(ingress *extensionsv1beta1.Ingress, client Client) (*fractionalWeightAllocator, error) {
|
|
||||||
servicePercentageWeights, err := getServicesPercentageWeights(ingress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceInstanceCounts, err := getServiceInstanceCounts(ingress, client)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceWeights := map[ingressService]int{}
|
|
||||||
|
|
||||||
for _, rule := range ingress.Spec.Rules {
|
|
||||||
// key: rule path string
|
|
||||||
// value: service names
|
|
||||||
fractionalPathServices := map[string][]string{}
|
|
||||||
|
|
||||||
// key: rule path string
|
|
||||||
// value: fractional percentage weight
|
|
||||||
fractionalPathWeights := map[string]percentageValue{}
|
|
||||||
|
|
||||||
for _, pa := range rule.HTTP.Paths {
|
|
||||||
if _, ok := fractionalPathWeights[pa.Path]; !ok {
|
|
||||||
fractionalPathWeights[pa.Path] = newPercentageValueFromFloat64(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if weight, ok := servicePercentageWeights[pa.Backend.ServiceName]; ok {
|
|
||||||
ingSvc := ingressService{
|
|
||||||
host: rule.Host,
|
|
||||||
path: pa.Path,
|
|
||||||
service: pa.Backend.ServiceName,
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceWeights[ingSvc] = weight.computeWeight(serviceInstanceCounts[ingSvc])
|
|
||||||
|
|
||||||
fractionalPathWeights[pa.Path] -= weight
|
|
||||||
|
|
||||||
if fractionalPathWeights[pa.Path].toFloat64() < 0 {
|
|
||||||
assignedWeight := newPercentageValueFromFloat64(1) - fractionalPathWeights[pa.Path]
|
|
||||||
return nil, fmt.Errorf("percentage value %s must not exceed 100%%", assignedWeight.String())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fractionalPathServices[pa.Path] = append(fractionalPathServices[pa.Path], pa.Backend.ServiceName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for pa, fractionalWeight := range fractionalPathWeights {
|
|
||||||
fractionalServices := fractionalPathServices[pa]
|
|
||||||
|
|
||||||
if len(fractionalServices) == 0 {
|
|
||||||
if fractionalWeight > 0 {
|
|
||||||
assignedWeight := newPercentageValueFromFloat64(1) - fractionalWeight
|
|
||||||
return nil, fmt.Errorf("the sum of weights(%s) in the path %s%s must be 100%% when no omitted fractional service left", assignedWeight.String(), rule.Host, pa)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
totalFractionalInstanceCount := 0
|
|
||||||
for _, svc := range fractionalServices {
|
|
||||||
totalFractionalInstanceCount += serviceInstanceCounts[ingressService{
|
|
||||||
host: rule.Host,
|
|
||||||
path: pa,
|
|
||||||
service: svc,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, svc := range fractionalServices {
|
|
||||||
ingSvc := ingressService{
|
|
||||||
host: rule.Host,
|
|
||||||
path: pa,
|
|
||||||
service: svc,
|
|
||||||
}
|
|
||||||
serviceWeights[ingSvc] = fractionalWeight.computeWeight(totalFractionalInstanceCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator := fractionalWeightAllocator(serviceWeights)
|
|
||||||
return &allocator, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fractionalWeightAllocator) getWeight(host, path, serviceName string) int {
|
|
||||||
return map[ingressService]int(*f)[ingressService{
|
|
||||||
host: host,
|
|
||||||
path: path,
|
|
||||||
service: serviceName,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
func getServicesPercentageWeights(ingress *extensionsv1beta1.Ingress) (map[string]percentageValue, error) {
|
|
||||||
percentageWeight := make(map[string]string)
|
|
||||||
|
|
||||||
annotationPercentageWeights := getAnnotationName(ingress.Annotations, annotationKubernetesServiceWeights)
|
|
||||||
if err := yaml.Unmarshal([]byte(ingress.Annotations[annotationPercentageWeights]), percentageWeight); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
servicesPercentageWeights := make(map[string]percentageValue)
|
|
||||||
for serviceName, percentageStr := range percentageWeight {
|
|
||||||
percentageValue, err := newPercentageValueFromString(percentageStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid percentage value %q", percentageStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
servicesPercentageWeights[serviceName] = percentageValue
|
|
||||||
}
|
|
||||||
return servicesPercentageWeights, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getServiceInstanceCounts(ingress *extensionsv1beta1.Ingress, client Client) (map[ingressService]int, error) {
|
|
||||||
serviceInstanceCounts := map[ingressService]int{}
|
|
||||||
|
|
||||||
for _, rule := range ingress.Spec.Rules {
|
|
||||||
for _, pa := range rule.HTTP.Paths {
|
|
||||||
svc, exists, err := client.GetService(ingress.Namespace, pa.Backend.ServiceName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get service %s/%s: %v", ingress.Namespace, pa.Backend.ServiceName, err)
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
return nil, fmt.Errorf("service not found for %s/%s", ingress.Namespace, pa.Backend.ServiceName)
|
|
||||||
}
|
|
||||||
if svc.Spec.Type == corev1.ServiceTypeExternalName {
|
|
||||||
// external-name service has only one instance b/c it will actually be interpreted as a DNS record
|
|
||||||
// instead of real server.
|
|
||||||
serviceInstanceCounts[ingressService{
|
|
||||||
host: rule.Host,
|
|
||||||
path: pa.Path,
|
|
||||||
service: pa.Backend.ServiceName,
|
|
||||||
}] = 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
count := 0
|
|
||||||
endpoints, exists, err := client.GetEndpoints(ingress.Namespace, pa.Backend.ServiceName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get endpoints %s/%s: %v", ingress.Namespace, pa.Backend.ServiceName, err)
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
return nil, fmt.Errorf("endpoints not found for %s/%s", ingress.Namespace, pa.Backend.ServiceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, subset := range endpoints.Subsets {
|
|
||||||
count += len(subset.Addresses)
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceInstanceCounts[ingressService{
|
|
||||||
host: rule.Host,
|
|
||||||
path: pa.Path,
|
|
||||||
service: pa.Backend.ServiceName,
|
|
||||||
}] += count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return serviceInstanceCounts, nil
|
|
||||||
}
|
|
|
@ -1,477 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestString(t *testing.T) {
|
|
||||||
pv1 := newPercentageValueFromFloat64(0.5)
|
|
||||||
pv2 := newPercentageValueFromFloat64(0.2)
|
|
||||||
pv3 := newPercentageValueFromFloat64(0.3)
|
|
||||||
f := fractionalWeightAllocator(
|
|
||||||
map[ingressService]int{
|
|
||||||
{
|
|
||||||
host: "host2",
|
|
||||||
path: "path2",
|
|
||||||
service: "service2",
|
|
||||||
}: int(pv2),
|
|
||||||
{
|
|
||||||
host: "host3",
|
|
||||||
path: "path3",
|
|
||||||
service: "service3",
|
|
||||||
}: int(pv3),
|
|
||||||
{
|
|
||||||
host: "host1",
|
|
||||||
path: "path1",
|
|
||||||
service: "service1",
|
|
||||||
}: int(pv1),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
expected := fmt.Sprintf("[service1: %s service2: %s service3: %s]", pv1, pv2, pv3)
|
|
||||||
actual := f.String()
|
|
||||||
assert.Equal(t, expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetServicesPercentageWeights(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
annotationValue string
|
|
||||||
expectError bool
|
|
||||||
expectedWeights map[string]percentageValue
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "empty annotation",
|
|
||||||
annotationValue: ``,
|
|
||||||
expectedWeights: map[string]percentageValue{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "50% fraction",
|
|
||||||
annotationValue: `
|
|
||||||
service1: 10%
|
|
||||||
service2: 20%
|
|
||||||
service3: 20%
|
|
||||||
`,
|
|
||||||
expectedWeights: map[string]percentageValue{
|
|
||||||
"service1": newPercentageValueFromFloat64(0.1),
|
|
||||||
"service2": newPercentageValueFromFloat64(0.2),
|
|
||||||
"service3": newPercentageValueFromFloat64(0.2),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "50% fraction with empty fraction",
|
|
||||||
annotationValue: `
|
|
||||||
service1: 10%
|
|
||||||
service2: 20%
|
|
||||||
service3: 20%
|
|
||||||
service4:
|
|
||||||
`,
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "50% fraction float form",
|
|
||||||
annotationValue: `
|
|
||||||
service1: 0.1
|
|
||||||
service2: 0.2
|
|
||||||
service3: 0.2
|
|
||||||
`,
|
|
||||||
expectedWeights: map[string]percentageValue{
|
|
||||||
"service1": newPercentageValueFromFloat64(0.001),
|
|
||||||
"service2": newPercentageValueFromFloat64(0.002),
|
|
||||||
"service3": newPercentageValueFromFloat64(0.002),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "no fraction",
|
|
||||||
annotationValue: `
|
|
||||||
service1: 10%
|
|
||||||
service2: 90%
|
|
||||||
`,
|
|
||||||
expectedWeights: map[string]percentageValue{
|
|
||||||
"service1": newPercentageValueFromFloat64(0.1),
|
|
||||||
"service2": newPercentageValueFromFloat64(0.9),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "extra weight specification",
|
|
||||||
annotationValue: `
|
|
||||||
service1: 90%
|
|
||||||
service5: 90%
|
|
||||||
`,
|
|
||||||
expectedWeights: map[string]percentageValue{
|
|
||||||
"service1": newPercentageValueFromFloat64(0.9),
|
|
||||||
"service5": newPercentageValueFromFloat64(0.9),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "malformed annotation",
|
|
||||||
annotationValue: `
|
|
||||||
service1- 90%
|
|
||||||
service5- 90%
|
|
||||||
`,
|
|
||||||
expectError: true,
|
|
||||||
expectedWeights: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "more than one hundred percentaged service",
|
|
||||||
annotationValue: `
|
|
||||||
service1: 100%
|
|
||||||
service2: 1%
|
|
||||||
`,
|
|
||||||
expectedWeights: map[string]percentageValue{
|
|
||||||
"service1": newPercentageValueFromFloat64(1),
|
|
||||||
"service2": newPercentageValueFromFloat64(0.01),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "incorrect percentage value",
|
|
||||||
annotationValue: `
|
|
||||||
service1: 1000%
|
|
||||||
`,
|
|
||||||
expectedWeights: map[string]percentageValue{
|
|
||||||
"service1": newPercentageValueFromFloat64(10),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
ingress := &extensionsv1beta1.Ingress{
|
|
||||||
ObjectMeta: v1.ObjectMeta{
|
|
||||||
Annotations: map[string]string{
|
|
||||||
annotationKubernetesServiceWeights: test.annotationValue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
weights, err := getServicesPercentageWeights(ingress)
|
|
||||||
|
|
||||||
if test.expectError {
|
|
||||||
require.Error(t, err)
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, test.expectedWeights, weights)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestComputeServiceWeights(t *testing.T) {
|
|
||||||
client := clientMock{
|
|
||||||
services: []*corev1.Service{
|
|
||||||
buildService(
|
|
||||||
sName("service1"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
),
|
|
||||||
buildService(
|
|
||||||
sName("service2"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
),
|
|
||||||
buildService(
|
|
||||||
sName("service3"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
),
|
|
||||||
buildService(
|
|
||||||
sName("service4"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
endpoints: []*corev1.Endpoints{
|
|
||||||
buildEndpoint(
|
|
||||||
eNamespace("testing"),
|
|
||||||
eName("service1"),
|
|
||||||
eUID("1"),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.10.0.1")),
|
|
||||||
ePorts(ePort(8080, ""))),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.21.0.2")),
|
|
||||||
ePorts(ePort(8080, ""))),
|
|
||||||
),
|
|
||||||
buildEndpoint(
|
|
||||||
eNamespace("testing"),
|
|
||||||
eName("service2"),
|
|
||||||
eUID("2"),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.10.0.3")),
|
|
||||||
ePorts(ePort(8080, ""))),
|
|
||||||
),
|
|
||||||
buildEndpoint(
|
|
||||||
eNamespace("testing"),
|
|
||||||
eName("service3"),
|
|
||||||
eUID("3"),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.10.0.4")),
|
|
||||||
ePorts(ePort(8080, ""))),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.21.0.5")),
|
|
||||||
ePorts(ePort(8080, ""))),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.21.0.6")),
|
|
||||||
ePorts(ePort(8080, ""))),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.21.0.7")),
|
|
||||||
ePorts(ePort(8080, ""))),
|
|
||||||
),
|
|
||||||
buildEndpoint(
|
|
||||||
eNamespace("testing"),
|
|
||||||
eName("service4"),
|
|
||||||
eUID("4"),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.10.0.7")),
|
|
||||||
ePorts(ePort(8080, ""))),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
ingress *extensionsv1beta1.Ingress
|
|
||||||
expectError bool
|
|
||||||
expectedWeights map[ingressService]percentageValue
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "1 path 2 service",
|
|
||||||
ingress: buildIngress(
|
|
||||||
iNamespace("testing"),
|
|
||||||
iAnnotation(annotationKubernetesServiceWeights, `
|
|
||||||
service1: 10%
|
|
||||||
`),
|
|
||||||
iRules(
|
|
||||||
iRule(iHost("foo.test"), iPaths(
|
|
||||||
onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
expectError: false,
|
|
||||||
expectedWeights: map[ingressService]percentageValue{
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service1",
|
|
||||||
}: newPercentageValueFromFloat64(0.05),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service2",
|
|
||||||
}: newPercentageValueFromFloat64(0.90),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "2 path 2 service",
|
|
||||||
ingress: buildIngress(
|
|
||||||
iNamespace("testing"),
|
|
||||||
iAnnotation(annotationKubernetesServiceWeights, `
|
|
||||||
service1: 60%
|
|
||||||
`),
|
|
||||||
iRules(
|
|
||||||
iRule(iHost("foo.test"), iPaths(
|
|
||||||
onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/bar"), iBackend("service3", intstr.FromInt(8080))),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
expectError: false,
|
|
||||||
expectedWeights: map[ingressService]percentageValue{
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service1",
|
|
||||||
}: newPercentageValueFromFloat64(0.30),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service2",
|
|
||||||
}: newPercentageValueFromFloat64(0.40),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/bar",
|
|
||||||
service: "service1",
|
|
||||||
}: newPercentageValueFromFloat64(0.30),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/bar",
|
|
||||||
service: "service3",
|
|
||||||
}: newPercentageValueFromFloat64(0.10),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "2 path 3 service",
|
|
||||||
ingress: buildIngress(
|
|
||||||
iNamespace("testing"),
|
|
||||||
iAnnotation(annotationKubernetesServiceWeights, `
|
|
||||||
service1: 20%
|
|
||||||
service3: 20%
|
|
||||||
`),
|
|
||||||
iRules(
|
|
||||||
iRule(iHost("foo.test"), iPaths(
|
|
||||||
onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/bar"), iBackend("service2", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/bar"), iBackend("service3", intstr.FromInt(8080))),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
expectError: false,
|
|
||||||
expectedWeights: map[ingressService]percentageValue{
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service1",
|
|
||||||
}: newPercentageValueFromFloat64(0.10),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service2",
|
|
||||||
}: newPercentageValueFromFloat64(0.80),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/bar",
|
|
||||||
service: "service3",
|
|
||||||
}: newPercentageValueFromFloat64(0.05),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/bar",
|
|
||||||
service: "service2",
|
|
||||||
}: newPercentageValueFromFloat64(0.80),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "1 path 4 service",
|
|
||||||
ingress: buildIngress(
|
|
||||||
iNamespace("testing"),
|
|
||||||
iAnnotation(annotationKubernetesServiceWeights, `
|
|
||||||
service1: 20%
|
|
||||||
service2: 40%
|
|
||||||
service3: 40%
|
|
||||||
`),
|
|
||||||
iRules(
|
|
||||||
iRule(iHost("foo.test"), iPaths(
|
|
||||||
onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/foo"), iBackend("service3", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/foo"), iBackend("service4", intstr.FromInt(8080))),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
expectError: false,
|
|
||||||
expectedWeights: map[ingressService]percentageValue{
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service1",
|
|
||||||
}: newPercentageValueFromFloat64(0.10),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service2",
|
|
||||||
}: newPercentageValueFromFloat64(0.40),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service3",
|
|
||||||
}: newPercentageValueFromFloat64(0.10),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service4",
|
|
||||||
}: newPercentageValueFromFloat64(0.00),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "2 path no service",
|
|
||||||
ingress: buildIngress(
|
|
||||||
iNamespace("testing"),
|
|
||||||
iAnnotation(annotationKubernetesServiceWeights, `
|
|
||||||
service1: 20%
|
|
||||||
service2: 40%
|
|
||||||
service3: 40%
|
|
||||||
`),
|
|
||||||
iRules(
|
|
||||||
iRule(iHost("foo.test"), iPaths(
|
|
||||||
onePath(iPath("/foo"), iBackend("noservice", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/bar"), iBackend("noservice", intstr.FromInt(8080))),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "2 path without weight",
|
|
||||||
ingress: buildIngress(
|
|
||||||
iNamespace("testing"),
|
|
||||||
iAnnotation(annotationKubernetesServiceWeights, ``),
|
|
||||||
iRules(
|
|
||||||
iRule(iHost("foo.test"), iPaths(
|
|
||||||
onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/bar"), iBackend("service2", intstr.FromInt(8080))),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
expectError: false,
|
|
||||||
expectedWeights: map[ingressService]percentageValue{
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/foo",
|
|
||||||
service: "service1",
|
|
||||||
}: newPercentageValueFromFloat64(0.50),
|
|
||||||
{
|
|
||||||
host: "foo.test",
|
|
||||||
path: "/bar",
|
|
||||||
service: "service2",
|
|
||||||
}: newPercentageValueFromFloat64(1.00),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "2 path overflow",
|
|
||||||
ingress: buildIngress(
|
|
||||||
iNamespace("testing"),
|
|
||||||
iAnnotation(annotationKubernetesServiceWeights, `
|
|
||||||
service1: 70%
|
|
||||||
service2: 80%
|
|
||||||
`),
|
|
||||||
iRules(
|
|
||||||
iRule(iHost("foo.test"), iPaths(
|
|
||||||
onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))),
|
|
||||||
onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
weightAllocator, err := newFractionalWeightAllocator(test.ingress, client)
|
|
||||||
if test.expectError {
|
|
||||||
require.Error(t, err)
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%v failed: %v", test.desc, err)
|
|
||||||
} else {
|
|
||||||
for ingSvc, percentage := range test.expectedWeights {
|
|
||||||
assert.Equal(t, int(percentage), weightAllocator.getWeight(ingSvc.host, ingSvc.path, ingSvc.service))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -41,6 +41,10 @@ func NewProviderAggregator(conf static.Providers) ProviderAggregator {
|
||||||
p.quietAddProvider(conf.Kubernetes)
|
p.quietAddProvider(conf.Kubernetes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.KubernetesCRD != nil {
|
||||||
|
p.quietAddProvider(conf.KubernetesCRD)
|
||||||
|
}
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildEndpoint(opts ...func(*corev1.Endpoints)) *corev1.Endpoints {
|
|
||||||
e := &corev1.Endpoints{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(e)
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func eNamespace(value string) func(*corev1.Endpoints) {
|
|
||||||
return func(i *corev1.Endpoints) {
|
|
||||||
i.Namespace = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eName(value string) func(*corev1.Endpoints) {
|
|
||||||
return func(i *corev1.Endpoints) {
|
|
||||||
i.Name = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eUID(value types.UID) func(*corev1.Endpoints) {
|
|
||||||
return func(i *corev1.Endpoints) {
|
|
||||||
i.UID = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func subset(opts ...func(*corev1.EndpointSubset)) func(*corev1.Endpoints) {
|
|
||||||
return func(e *corev1.Endpoints) {
|
|
||||||
s := &corev1.EndpointSubset{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
e.Subsets = append(e.Subsets, *s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eAddresses(opts ...func(*corev1.EndpointAddress)) func(*corev1.EndpointSubset) {
|
|
||||||
return func(subset *corev1.EndpointSubset) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
a := &corev1.EndpointAddress{}
|
|
||||||
opt(a)
|
|
||||||
subset.Addresses = append(subset.Addresses, *a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eAddress(ip string) func(*corev1.EndpointAddress) {
|
|
||||||
return func(address *corev1.EndpointAddress) {
|
|
||||||
address.IP = ip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func eAddressWithTargetRef(targetRef, ip string) func(*corev1.EndpointAddress) {
|
|
||||||
return func(address *corev1.EndpointAddress) {
|
|
||||||
address.TargetRef = &corev1.ObjectReference{Name: targetRef}
|
|
||||||
address.IP = ip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ePorts(opts ...func(port *corev1.EndpointPort)) func(*corev1.EndpointSubset) {
|
|
||||||
return func(spec *corev1.EndpointSubset) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
p := &corev1.EndpointPort{}
|
|
||||||
opt(p)
|
|
||||||
spec.Ports = append(spec.Ports, *p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ePort(port int32, name string) func(*corev1.EndpointPort) {
|
|
||||||
return func(sp *corev1.EndpointPort) {
|
|
||||||
sp.Port = port
|
|
||||||
sp.Name = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
func TestBuildEndpoint(t *testing.T) {
|
|
||||||
actual := buildEndpoint(
|
|
||||||
eNamespace("testing"),
|
|
||||||
eName("service3"),
|
|
||||||
eUID("3"),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.15.0.1")),
|
|
||||||
ePorts(
|
|
||||||
ePort(8080, "http"),
|
|
||||||
ePort(8443, "https"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subset(
|
|
||||||
eAddresses(eAddress("10.15.0.2")),
|
|
||||||
ePorts(
|
|
||||||
ePort(9080, "http"),
|
|
||||||
ePort(9443, "https"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleEndpoint1(), actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleEndpoint1() *corev1.Endpoints {
|
|
||||||
return &corev1.Endpoints{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "service3",
|
|
||||||
UID: "3",
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Subsets: []corev1.EndpointSubset{
|
|
||||||
{
|
|
||||||
Addresses: []corev1.EndpointAddress{
|
|
||||||
{
|
|
||||||
IP: "10.15.0.1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Ports: []corev1.EndpointPort{
|
|
||||||
{
|
|
||||||
Name: "http",
|
|
||||||
Port: 8080,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "https",
|
|
||||||
Port: 8443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Addresses: []corev1.EndpointAddress{
|
|
||||||
{
|
|
||||||
IP: "10.15.0.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Ports: []corev1.EndpointPort{
|
|
||||||
{
|
|
||||||
Name: "http",
|
|
||||||
Port: 9080,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "https",
|
|
||||||
Port: 9443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildIngress(opts ...func(*extensionsv1beta1.Ingress)) *extensionsv1beta1.Ingress {
|
|
||||||
i := &extensionsv1beta1.Ingress{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(i)
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func iNamespace(value string) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
i.Namespace = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iAnnotation(name string, value string) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
if i.Annotations == nil {
|
|
||||||
i.Annotations = make(map[string]string)
|
|
||||||
}
|
|
||||||
i.Annotations[name] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iRules(opts ...func(*extensionsv1beta1.IngressSpec)) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
s := &extensionsv1beta1.IngressSpec{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
i.Spec = *s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iSpecBackends(opts ...func(*extensionsv1beta1.IngressSpec)) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
s := &extensionsv1beta1.IngressSpec{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
i.Spec = *s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iSpecBackend(opts ...func(*extensionsv1beta1.IngressBackend)) func(*extensionsv1beta1.IngressSpec) {
|
|
||||||
return func(s *extensionsv1beta1.IngressSpec) {
|
|
||||||
p := &extensionsv1beta1.IngressBackend{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(p)
|
|
||||||
}
|
|
||||||
s.Backend = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iIngressBackend(name string, port intstr.IntOrString) func(*extensionsv1beta1.IngressBackend) {
|
|
||||||
return func(p *extensionsv1beta1.IngressBackend) {
|
|
||||||
p.ServiceName = name
|
|
||||||
p.ServicePort = port
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iRule(opts ...func(*extensionsv1beta1.IngressRule)) func(*extensionsv1beta1.IngressSpec) {
|
|
||||||
return func(spec *extensionsv1beta1.IngressSpec) {
|
|
||||||
r := &extensionsv1beta1.IngressRule{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(r)
|
|
||||||
}
|
|
||||||
spec.Rules = append(spec.Rules, *r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iHost(name string) func(*extensionsv1beta1.IngressRule) {
|
|
||||||
return func(rule *extensionsv1beta1.IngressRule) {
|
|
||||||
rule.Host = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iPaths(opts ...func(*extensionsv1beta1.HTTPIngressRuleValue)) func(*extensionsv1beta1.IngressRule) {
|
|
||||||
return func(rule *extensionsv1beta1.IngressRule) {
|
|
||||||
rule.HTTP = &extensionsv1beta1.HTTPIngressRuleValue{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(rule.HTTP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func onePath(opts ...func(*extensionsv1beta1.HTTPIngressPath)) func(*extensionsv1beta1.HTTPIngressRuleValue) {
|
|
||||||
return func(irv *extensionsv1beta1.HTTPIngressRuleValue) {
|
|
||||||
p := &extensionsv1beta1.HTTPIngressPath{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(p)
|
|
||||||
}
|
|
||||||
irv.Paths = append(irv.Paths, *p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iPath(name string) func(*extensionsv1beta1.HTTPIngressPath) {
|
|
||||||
return func(p *extensionsv1beta1.HTTPIngressPath) {
|
|
||||||
p.Path = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iBackend(name string, port intstr.IntOrString) func(*extensionsv1beta1.HTTPIngressPath) {
|
|
||||||
return func(p *extensionsv1beta1.HTTPIngressPath) {
|
|
||||||
p.Backend = extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: name,
|
|
||||||
ServicePort: port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iTLSes(opts ...func(*extensionsv1beta1.IngressTLS)) func(*extensionsv1beta1.Ingress) {
|
|
||||||
return func(i *extensionsv1beta1.Ingress) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
iTLS := extensionsv1beta1.IngressTLS{}
|
|
||||||
opt(&iTLS)
|
|
||||||
i.Spec.TLS = append(i.Spec.TLS, iTLS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iTLS(secret string, hosts ...string) func(*extensionsv1beta1.IngressTLS) {
|
|
||||||
return func(i *extensionsv1beta1.IngressTLS) {
|
|
||||||
i.SecretName = secret
|
|
||||||
i.Hosts = hosts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
func TestBuildIngress(t *testing.T) {
|
|
||||||
i := buildIngress(
|
|
||||||
iNamespace("testing"),
|
|
||||||
iRules(
|
|
||||||
iRule(iHost("foo"), iPaths(
|
|
||||||
onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))),
|
|
||||||
onePath(iPath("/namedthing"), iBackend("service4", intstr.FromString("https")))),
|
|
||||||
),
|
|
||||||
iRule(iHost("bar"), iPaths(
|
|
||||||
onePath(iBackend("service3", intstr.FromString("https"))),
|
|
||||||
onePath(iBackend("service2", intstr.FromInt(802))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
iTLSes(
|
|
||||||
iTLS("tls-secret", "foo"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleIngress(), i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleIngress() *extensionsv1beta1.Ingress {
|
|
||||||
return &extensionsv1beta1.Ingress{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Spec: extensionsv1beta1.IngressSpec{
|
|
||||||
Rules: []extensionsv1beta1.IngressRule{
|
|
||||||
{
|
|
||||||
Host: "foo",
|
|
||||||
IngressRuleValue: extensionsv1beta1.IngressRuleValue{
|
|
||||||
HTTP: &extensionsv1beta1.HTTPIngressRuleValue{
|
|
||||||
Paths: []extensionsv1beta1.HTTPIngressPath{
|
|
||||||
{
|
|
||||||
Path: "/bar",
|
|
||||||
Backend: extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: "service1",
|
|
||||||
ServicePort: intstr.FromInt(80),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Path: "/namedthing",
|
|
||||||
Backend: extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: "service4",
|
|
||||||
ServicePort: intstr.FromString("https"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Host: "bar",
|
|
||||||
IngressRuleValue: extensionsv1beta1.IngressRuleValue{
|
|
||||||
HTTP: &extensionsv1beta1.HTTPIngressRuleValue{
|
|
||||||
Paths: []extensionsv1beta1.HTTPIngressPath{
|
|
||||||
{
|
|
||||||
Backend: extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: "service3",
|
|
||||||
ServicePort: intstr.FromString("https"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Backend: extensionsv1beta1.IngressBackend{
|
|
||||||
ServiceName: "service2",
|
|
||||||
ServicePort: intstr.FromInt(802),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TLS: []extensionsv1beta1.IngressTLS{
|
|
||||||
{
|
|
||||||
Hosts: []string{"foo"},
|
|
||||||
SecretName: "tls-secret",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,232 +0,0 @@
|
||||||
package kubernetes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildService(opts ...func(*corev1.Service)) *corev1.Service {
|
|
||||||
s := &corev1.Service{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(s)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func sNamespace(value string) func(*corev1.Service) {
|
|
||||||
return func(i *corev1.Service) {
|
|
||||||
i.Namespace = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sName(value string) func(*corev1.Service) {
|
|
||||||
return func(i *corev1.Service) {
|
|
||||||
i.Name = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sUID(value types.UID) func(*corev1.Service) {
|
|
||||||
return func(i *corev1.Service) {
|
|
||||||
i.UID = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sAnnotation(name string, value string) func(*corev1.Service) {
|
|
||||||
return func(s *corev1.Service) {
|
|
||||||
if s.Annotations == nil {
|
|
||||||
s.Annotations = make(map[string]string)
|
|
||||||
}
|
|
||||||
s.Annotations[name] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sSpec(opts ...func(*corev1.ServiceSpec)) func(*corev1.Service) {
|
|
||||||
return func(s *corev1.Service) {
|
|
||||||
spec := &corev1.ServiceSpec{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(spec)
|
|
||||||
}
|
|
||||||
s.Spec = *spec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sLoadBalancerStatus(opts ...func(*corev1.LoadBalancerStatus)) func(service *corev1.Service) {
|
|
||||||
return func(s *corev1.Service) {
|
|
||||||
loadBalancer := &corev1.LoadBalancerStatus{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
if opt != nil {
|
|
||||||
opt(loadBalancer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.Status = corev1.ServiceStatus{
|
|
||||||
LoadBalancer: *loadBalancer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sLoadBalancerIngress(ip string, hostname string) func(*corev1.LoadBalancerStatus) {
|
|
||||||
return func(status *corev1.LoadBalancerStatus) {
|
|
||||||
ingress := corev1.LoadBalancerIngress{
|
|
||||||
IP: ip,
|
|
||||||
Hostname: hostname,
|
|
||||||
}
|
|
||||||
status.Ingress = append(status.Ingress, ingress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func clusterIP(ip string) func(*corev1.ServiceSpec) {
|
|
||||||
return func(spec *corev1.ServiceSpec) {
|
|
||||||
spec.ClusterIP = ip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sType(value corev1.ServiceType) func(*corev1.ServiceSpec) {
|
|
||||||
return func(spec *corev1.ServiceSpec) {
|
|
||||||
spec.Type = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sExternalName(name string) func(*corev1.ServiceSpec) {
|
|
||||||
return func(spec *corev1.ServiceSpec) {
|
|
||||||
spec.ExternalName = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sPorts(opts ...func(*corev1.ServicePort)) func(*corev1.ServiceSpec) {
|
|
||||||
return func(spec *corev1.ServiceSpec) {
|
|
||||||
for _, opt := range opts {
|
|
||||||
p := &corev1.ServicePort{}
|
|
||||||
opt(p)
|
|
||||||
spec.Ports = append(spec.Ports, *p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sPort(port int32, name string) func(*corev1.ServicePort) {
|
|
||||||
return func(sp *corev1.ServicePort) {
|
|
||||||
sp.Port = port
|
|
||||||
sp.Name = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
func TestBuildService(t *testing.T) {
|
|
||||||
actual1 := buildService(
|
|
||||||
sName("service1"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
sUID("1"),
|
|
||||||
sSpec(
|
|
||||||
clusterIP("10.0.0.1"),
|
|
||||||
sPorts(sPort(80, "")),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleService1(), actual1)
|
|
||||||
|
|
||||||
actual2 := buildService(
|
|
||||||
sName("service2"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
sUID("2"),
|
|
||||||
sSpec(
|
|
||||||
clusterIP("10.0.0.2"),
|
|
||||||
sType("ExternalName"),
|
|
||||||
sExternalName("example.com"),
|
|
||||||
sPorts(
|
|
||||||
sPort(80, "http"),
|
|
||||||
sPort(443, "https"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleService2(), actual2)
|
|
||||||
|
|
||||||
actual3 := buildService(
|
|
||||||
sName("service3"),
|
|
||||||
sNamespace("testing"),
|
|
||||||
sUID("3"),
|
|
||||||
sSpec(
|
|
||||||
clusterIP("10.0.0.3"),
|
|
||||||
sType("ExternalName"),
|
|
||||||
sExternalName("example.com"),
|
|
||||||
sPorts(
|
|
||||||
sPort(8080, "http"),
|
|
||||||
sPort(8443, "https"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.EqualValues(t, sampleService3(), actual3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleService1() *corev1.Service {
|
|
||||||
return &corev1.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "service1",
|
|
||||||
UID: "1",
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Spec: corev1.ServiceSpec{
|
|
||||||
ClusterIP: "10.0.0.1",
|
|
||||||
Ports: []corev1.ServicePort{
|
|
||||||
{
|
|
||||||
Port: 80,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleService2() *corev1.Service {
|
|
||||||
return &corev1.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "service2",
|
|
||||||
UID: "2",
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Spec: corev1.ServiceSpec{
|
|
||||||
ClusterIP: "10.0.0.2",
|
|
||||||
Type: "ExternalName",
|
|
||||||
ExternalName: "example.com",
|
|
||||||
Ports: []corev1.ServicePort{
|
|
||||||
{
|
|
||||||
Name: "http",
|
|
||||||
Port: 80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "https",
|
|
||||||
Port: 443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sampleService3() *corev1.Service {
|
|
||||||
return &corev1.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "service3",
|
|
||||||
UID: "3",
|
|
||||||
Namespace: "testing",
|
|
||||||
},
|
|
||||||
Spec: corev1.ServiceSpec{
|
|
||||||
ClusterIP: "10.0.0.3",
|
|
||||||
Type: "ExternalName",
|
|
||||||
ExternalName: "example.com",
|
|
||||||
Ports: []corev1.ServicePort{
|
|
||||||
{
|
|
||||||
Name: "http",
|
|
||||||
Port: 8080,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "https",
|
|
||||||
Port: 8443,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package kubernetes
|
package crd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -7,6 +7,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containous/traefik/old/log"
|
"github.com/containous/traefik/old/log"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/crd/generated/informers/externalversions"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/k8s"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
kubeerror "k8s.io/apimachinery/pkg/api/errors"
|
kubeerror "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -16,6 +20,7 @@ import (
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const resyncPeriod = 10 * time.Minute
|
const resyncPeriod = 10 * time.Minute
|
||||||
|
@ -40,7 +45,11 @@ func (reh *resourceEventHandler) OnDelete(obj interface{}) {
|
||||||
// WatchAll starts the watch of the Provider resources and updates the stores.
|
// WatchAll starts the watch of the Provider resources and updates the stores.
|
||||||
// The stores can then be accessed via the Get* functions.
|
// The stores can then be accessed via the Get* functions.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
WatchAll(namespaces Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error)
|
WatchAll(namespaces k8s.Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error)
|
||||||
|
|
||||||
|
GetIngressRoutes() []*v1alpha1.IngressRoute
|
||||||
|
GetMiddlewares() []*v1alpha1.Middleware
|
||||||
|
|
||||||
GetIngresses() []*extensionsv1beta1.Ingress
|
GetIngresses() []*extensionsv1beta1.Ingress
|
||||||
GetService(namespace, name string) (*corev1.Service, bool, error)
|
GetService(namespace, name string) (*corev1.Service, bool, error)
|
||||||
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
|
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
|
||||||
|
@ -48,24 +57,46 @@ type Client interface {
|
||||||
UpdateIngressStatus(namespace, name, ip, hostname string) error
|
UpdateIngressStatus(namespace, name, ip, hostname string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientImpl struct {
|
// TODO: add tests for the clientWrapper (and its methods) itself.
|
||||||
clientset *kubernetes.Clientset
|
type clientWrapper struct {
|
||||||
factories map[string]informers.SharedInformerFactory
|
csCrd *versioned.Clientset
|
||||||
|
csKube *kubernetes.Clientset
|
||||||
|
|
||||||
|
factoriesCrd map[string]externalversions.SharedInformerFactory
|
||||||
|
factoriesKube map[string]informers.SharedInformerFactory
|
||||||
|
|
||||||
ingressLabelSelector labels.Selector
|
ingressLabelSelector labels.Selector
|
||||||
|
|
||||||
isNamespaceAll bool
|
isNamespaceAll bool
|
||||||
watchedNamespaces Namespaces
|
watchedNamespaces k8s.Namespaces
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClientImpl(clientset *kubernetes.Clientset) *clientImpl {
|
func createClientFromConfig(c *rest.Config) (*clientWrapper, error) {
|
||||||
return &clientImpl{
|
csCrd, err := versioned.NewForConfig(c)
|
||||||
clientset: clientset,
|
if err != nil {
|
||||||
factories: make(map[string]informers.SharedInformerFactory),
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
csKube, err := kubernetes.NewForConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newClientImpl(csKube, csCrd), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClientImpl(csKube *kubernetes.Clientset, csCrd *versioned.Clientset) *clientWrapper {
|
||||||
|
return &clientWrapper{
|
||||||
|
csCrd: csCrd,
|
||||||
|
csKube: csKube,
|
||||||
|
factoriesCrd: make(map[string]externalversions.SharedInformerFactory),
|
||||||
|
factoriesKube: make(map[string]informers.SharedInformerFactory),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// newInClusterClient returns a new Provider client that is expected to run
|
// newInClusterClient returns a new Provider client that is expected to run
|
||||||
// inside the cluster.
|
// inside the cluster.
|
||||||
func newInClusterClient(endpoint string) (*clientImpl, error) {
|
func newInClusterClient(endpoint string) (*clientWrapper, error) {
|
||||||
config, err := rest.InClusterConfig()
|
config, err := rest.InClusterConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create in-cluster configuration: %s", err)
|
return nil, fmt.Errorf("failed to create in-cluster configuration: %s", err)
|
||||||
|
@ -78,10 +109,18 @@ func newInClusterClient(endpoint string) (*clientImpl, error) {
|
||||||
return createClientFromConfig(config)
|
return createClientFromConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newExternalClusterClientFromFile(file string) (*clientWrapper, error) {
|
||||||
|
configFromFlags, err := clientcmd.BuildConfigFromFlags("", file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return createClientFromConfig(configFromFlags)
|
||||||
|
}
|
||||||
|
|
||||||
// newExternalClusterClient returns a new Provider client that may run outside
|
// newExternalClusterClient returns a new Provider client that may run outside
|
||||||
// of the cluster.
|
// of the cluster.
|
||||||
// The endpoint parameter must not be empty.
|
// The endpoint parameter must not be empty.
|
||||||
func newExternalClusterClient(endpoint, token, caFilePath string) (*clientImpl, error) {
|
func newExternalClusterClient(endpoint, token, caFilePath string) (*clientWrapper, error) {
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
return nil, errors.New("endpoint missing for external cluster client")
|
return nil, errors.New("endpoint missing for external cluster client")
|
||||||
}
|
}
|
||||||
|
@ -103,41 +142,44 @@ func newExternalClusterClient(endpoint, token, caFilePath string) (*clientImpl,
|
||||||
return createClientFromConfig(config)
|
return createClientFromConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createClientFromConfig(c *rest.Config) (*clientImpl, error) {
|
|
||||||
clientset, err := kubernetes.NewForConfig(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return newClientImpl(clientset), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WatchAll starts namespace-specific controllers for all relevant kinds.
|
// WatchAll starts namespace-specific controllers for all relevant kinds.
|
||||||
func (c *clientImpl) WatchAll(namespaces Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
func (c *clientWrapper) WatchAll(namespaces k8s.Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
||||||
eventCh := make(chan interface{}, 1)
|
eventCh := make(chan interface{}, 1)
|
||||||
|
eventHandler := c.newResourceEventHandler(eventCh)
|
||||||
|
|
||||||
if len(namespaces) == 0 {
|
if len(namespaces) == 0 {
|
||||||
namespaces = Namespaces{metav1.NamespaceAll}
|
namespaces = k8s.Namespaces{metav1.NamespaceAll}
|
||||||
c.isNamespaceAll = true
|
c.isNamespaceAll = true
|
||||||
}
|
}
|
||||||
|
|
||||||
c.watchedNamespaces = namespaces
|
c.watchedNamespaces = namespaces
|
||||||
|
|
||||||
eventHandler := c.newResourceEventHandler(eventCh)
|
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
factory := informers.NewFilteredSharedInformerFactory(c.clientset, resyncPeriod, ns, nil)
|
factoryCrd := externalversions.NewSharedInformerFactoryWithOptions(c.csCrd, resyncPeriod, externalversions.WithNamespace(ns))
|
||||||
factory.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
|
factoryCrd.Traefik().V1alpha1().IngressRoutes().Informer().AddEventHandler(eventHandler)
|
||||||
factory.Core().V1().Services().Informer().AddEventHandler(eventHandler)
|
factoryCrd.Traefik().V1alpha1().Middlewares().Informer().AddEventHandler(eventHandler)
|
||||||
factory.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
|
|
||||||
c.factories[ns] = factory
|
factoryKube := informers.NewFilteredSharedInformerFactory(c.csKube, resyncPeriod, ns, nil)
|
||||||
|
factoryKube.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
|
||||||
|
factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
|
||||||
|
factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
|
||||||
|
|
||||||
|
c.factoriesCrd[ns] = factoryCrd
|
||||||
|
c.factoriesKube[ns] = factoryKube
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
c.factories[ns].Start(stopCh)
|
c.factoriesCrd[ns].Start(stopCh)
|
||||||
|
c.factoriesKube[ns].Start(stopCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
for t, ok := range c.factories[ns].WaitForCacheSync(stopCh) {
|
for t, ok := range c.factoriesCrd[ns].WaitForCacheSync(stopCh) {
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for t, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
|
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
|
||||||
}
|
}
|
||||||
|
@ -149,17 +191,45 @@ func (c *clientImpl) WatchAll(namespaces Namespaces, stopCh <-chan struct{}) (<-
|
||||||
// https://github.com/containous/traefik/issues/1784 should improve the
|
// https://github.com/containous/traefik/issues/1784 should improve the
|
||||||
// situation here in the future.
|
// situation here in the future.
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
c.factories[ns].Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
|
c.factoriesKube[ns].Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
|
||||||
c.factories[ns].Start(stopCh)
|
c.factoriesKube[ns].Start(stopCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
return eventCh, nil
|
return eventCh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *clientWrapper) GetIngressRoutes() []*v1alpha1.IngressRoute {
|
||||||
|
var result []*v1alpha1.IngressRoute
|
||||||
|
|
||||||
|
for ns, factory := range c.factoriesCrd {
|
||||||
|
ings, err := factory.Traefik().V1alpha1().IngressRoutes().Lister().List(c.ingressLabelSelector)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to list ingresses in namespace %s: %s", ns, err)
|
||||||
|
}
|
||||||
|
result = append(result, ings...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *clientWrapper) GetMiddlewares() []*v1alpha1.Middleware {
|
||||||
|
var result []*v1alpha1.Middleware
|
||||||
|
|
||||||
|
for ns, factory := range c.factoriesCrd {
|
||||||
|
ings, err := factory.Traefik().V1alpha1().Middlewares().Lister().List(c.ingressLabelSelector)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to list ingresses in namespace %s: %s", ns, err)
|
||||||
|
}
|
||||||
|
result = append(result, ings...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// GetIngresses returns all Ingresses for observed namespaces in the cluster.
|
// GetIngresses returns all Ingresses for observed namespaces in the cluster.
|
||||||
func (c *clientImpl) GetIngresses() []*extensionsv1beta1.Ingress {
|
func (c *clientWrapper) GetIngresses() []*extensionsv1beta1.Ingress {
|
||||||
var result []*extensionsv1beta1.Ingress
|
var result []*extensionsv1beta1.Ingress
|
||||||
for ns, factory := range c.factories {
|
for ns, factory := range c.factoriesKube {
|
||||||
ings, err := factory.Extensions().V1beta1().Ingresses().Lister().List(c.ingressLabelSelector)
|
ings, err := factory.Extensions().V1beta1().Ingresses().Lister().List(c.ingressLabelSelector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to list ingresses in namespace %s: %s", ns, err)
|
log.Errorf("Failed to list ingresses in namespace %s: %s", ns, err)
|
||||||
|
@ -170,12 +240,12 @@ func (c *clientImpl) GetIngresses() []*extensionsv1beta1.Ingress {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIngressStatus updates an Ingress with a provided status.
|
// UpdateIngressStatus updates an Ingress with a provided status.
|
||||||
func (c *clientImpl) UpdateIngressStatus(namespace, name, ip, hostname string) error {
|
func (c *clientWrapper) UpdateIngressStatus(namespace, name, ip, hostname string) error {
|
||||||
if !c.isWatchedNamespace(namespace) {
|
if !c.isWatchedNamespace(namespace) {
|
||||||
return fmt.Errorf("failed to get ingress %s/%s: namespace is not within watched namespaces", namespace, name)
|
return fmt.Errorf("failed to get ingress %s/%s: namespace is not within watched namespaces", namespace, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
ing, err := c.factories[c.lookupNamespace(namespace)].Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).Get(name)
|
ing, err := c.factoriesKube[c.lookupNamespace(namespace)].Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get ingress %s/%s: %v", namespace, name, err)
|
return fmt.Errorf("failed to get ingress %s/%s: %v", namespace, name, err)
|
||||||
}
|
}
|
||||||
|
@ -190,7 +260,7 @@ func (c *clientImpl) UpdateIngressStatus(namespace, name, ip, hostname string) e
|
||||||
ingCopy := ing.DeepCopy()
|
ingCopy := ing.DeepCopy()
|
||||||
ingCopy.Status = extensionsv1beta1.IngressStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{IP: ip, Hostname: hostname}}}}
|
ingCopy.Status = extensionsv1beta1.IngressStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: []corev1.LoadBalancerIngress{{IP: ip, Hostname: hostname}}}}
|
||||||
|
|
||||||
_, err = c.clientset.ExtensionsV1beta1().Ingresses(ingCopy.Namespace).UpdateStatus(ingCopy)
|
_, err = c.csKube.ExtensionsV1beta1().Ingresses(ingCopy.Namespace).UpdateStatus(ingCopy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to update ingress status %s/%s: %v", namespace, name, err)
|
return fmt.Errorf("failed to update ingress status %s/%s: %v", namespace, name, err)
|
||||||
}
|
}
|
||||||
|
@ -199,34 +269,34 @@ func (c *clientImpl) UpdateIngressStatus(namespace, name, ip, hostname string) e
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetService returns the named service from the given namespace.
|
// GetService returns the named service from the given namespace.
|
||||||
func (c *clientImpl) GetService(namespace, name string) (*corev1.Service, bool, error) {
|
func (c *clientWrapper) GetService(namespace, name string) (*corev1.Service, bool, error) {
|
||||||
if !c.isWatchedNamespace(namespace) {
|
if !c.isWatchedNamespace(namespace) {
|
||||||
return nil, false, fmt.Errorf("failed to get service %s/%s: namespace is not within watched namespaces", namespace, name)
|
return nil, false, fmt.Errorf("failed to get service %s/%s: namespace is not within watched namespaces", namespace, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
service, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Services().Lister().Services(namespace).Get(name)
|
service, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Services().Lister().Services(namespace).Get(name)
|
||||||
exist, err := translateNotFoundError(err)
|
exist, err := translateNotFoundError(err)
|
||||||
return service, exist, err
|
return service, exist, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEndpoints returns the named endpoints from the given namespace.
|
// GetEndpoints returns the named endpoints from the given namespace.
|
||||||
func (c *clientImpl) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) {
|
func (c *clientWrapper) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) {
|
||||||
if !c.isWatchedNamespace(namespace) {
|
if !c.isWatchedNamespace(namespace) {
|
||||||
return nil, false, fmt.Errorf("failed to get endpoints %s/%s: namespace is not within watched namespaces", namespace, name)
|
return nil, false, fmt.Errorf("failed to get endpoints %s/%s: namespace is not within watched namespaces", namespace, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoint, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Endpoints().Lister().Endpoints(namespace).Get(name)
|
endpoint, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Endpoints().Lister().Endpoints(namespace).Get(name)
|
||||||
exist, err := translateNotFoundError(err)
|
exist, err := translateNotFoundError(err)
|
||||||
return endpoint, exist, err
|
return endpoint, exist, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSecret returns the named secret from the given namespace.
|
// GetSecret returns the named secret from the given namespace.
|
||||||
func (c *clientImpl) GetSecret(namespace, name string) (*corev1.Secret, bool, error) {
|
func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool, error) {
|
||||||
if !c.isWatchedNamespace(namespace) {
|
if !c.isWatchedNamespace(namespace) {
|
||||||
return nil, false, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name)
|
return nil, false, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
secret, err := c.factories[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name)
|
secret, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name)
|
||||||
exist, err := translateNotFoundError(err)
|
exist, err := translateNotFoundError(err)
|
||||||
return secret, exist, err
|
return secret, exist, err
|
||||||
}
|
}
|
||||||
|
@ -237,14 +307,14 @@ func (c *clientImpl) GetSecret(namespace, name string) (*corev1.Secret, bool, er
|
||||||
// The distinction is necessary because we index all informers on the special
|
// The distinction is necessary because we index all informers on the special
|
||||||
// identifier iff all-namespaces are requested but receive specific namespace
|
// identifier iff all-namespaces are requested but receive specific namespace
|
||||||
// identifiers from the Kubernetes API, so we have to bridge this gap.
|
// identifiers from the Kubernetes API, so we have to bridge this gap.
|
||||||
func (c *clientImpl) lookupNamespace(ns string) string {
|
func (c *clientWrapper) lookupNamespace(ns string) string {
|
||||||
if c.isNamespaceAll {
|
if c.isNamespaceAll {
|
||||||
return metav1.NamespaceAll
|
return metav1.NamespaceAll
|
||||||
}
|
}
|
||||||
return ns
|
return ns
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientImpl) newResourceEventHandler(events chan<- interface{}) cache.ResourceEventHandler {
|
func (c *clientWrapper) newResourceEventHandler(events chan<- interface{}) cache.ResourceEventHandler {
|
||||||
return &cache.FilteringResourceEventHandler{
|
return &cache.FilteringResourceEventHandler{
|
||||||
FilterFunc: func(obj interface{}) bool {
|
FilterFunc: func(obj interface{}) bool {
|
||||||
// Ignore Ingresses that do not match our custom label selector.
|
// Ignore Ingresses that do not match our custom label selector.
|
||||||
|
@ -278,8 +348,8 @@ func translateNotFoundError(err error) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// isWatchedNamespace checks to ensure that the namespace is being watched before we request
|
// isWatchedNamespace checks to ensure that the namespace is being watched before we request
|
||||||
// it to ensure we don't panic by requesting an out-of-watch object
|
// it to ensure we don't panic by requesting an out-of-watch object.
|
||||||
func (c *clientImpl) isWatchedNamespace(ns string) bool {
|
func (c *clientWrapper) isWatchedNamespace(ns string) bool {
|
||||||
if c.isNamespaceAll {
|
if c.isNamespaceAll {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
|
@ -1,21 +1,83 @@
|
||||||
package kubernetes
|
package crd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/k8s"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
|
v1beta12 "k8s.io/api/extensions/v1beta1"
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ Client = (*clientMock)(nil)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// required by k8s.MustParseYaml
|
||||||
|
err := v1alpha1.AddToScheme(scheme.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type clientMock struct {
|
type clientMock struct {
|
||||||
ingresses []*extensionsv1beta1.Ingress
|
ingresses []*extensionsv1beta1.Ingress
|
||||||
services []*corev1.Service
|
services []*corev1.Service
|
||||||
secrets []*corev1.Secret
|
secrets []*corev1.Secret
|
||||||
endpoints []*corev1.Endpoints
|
endpoints []*corev1.Endpoints
|
||||||
watchChan chan interface{}
|
|
||||||
|
|
||||||
apiServiceError error
|
apiServiceError error
|
||||||
apiSecretError error
|
apiSecretError error
|
||||||
apiEndpointsError error
|
apiEndpointsError error
|
||||||
apiIngressStatusError error
|
apiIngressStatusError error
|
||||||
|
|
||||||
|
ingressRoutes []*v1alpha1.IngressRoute
|
||||||
|
middlewares []*v1alpha1.Middleware
|
||||||
|
|
||||||
|
watchChan chan interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClientMock(paths ...string) clientMock {
|
||||||
|
var c clientMock
|
||||||
|
|
||||||
|
for _, path := range paths {
|
||||||
|
yamlContent, err := ioutil.ReadFile("./fixtures/" + path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
k8sObjects := k8s.MustParseYaml(yamlContent)
|
||||||
|
for _, obj := range k8sObjects {
|
||||||
|
switch o := obj.(type) {
|
||||||
|
case *corev1.Service:
|
||||||
|
c.services = append(c.services, o)
|
||||||
|
case *corev1.Endpoints:
|
||||||
|
c.endpoints = append(c.endpoints, o)
|
||||||
|
case *v1alpha1.IngressRoute:
|
||||||
|
c.ingressRoutes = append(c.ingressRoutes, o)
|
||||||
|
case *v1alpha1.Middleware:
|
||||||
|
c.middlewares = append(c.middlewares, o)
|
||||||
|
case *v1beta12.Ingress:
|
||||||
|
c.ingresses = append(c.ingresses, o)
|
||||||
|
case *corev1.Secret:
|
||||||
|
c.secrets = append(c.secrets, o)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unknown runtime object %+v %T", o, o))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c clientMock) GetIngressRoutes() []*v1alpha1.IngressRoute {
|
||||||
|
return c.ingressRoutes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c clientMock) GetMiddlewares() []*v1alpha1.Middleware {
|
||||||
|
return c.middlewares
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c clientMock) GetIngresses() []*extensionsv1beta1.Ingress {
|
func (c clientMock) GetIngresses() []*extensionsv1beta1.Ingress {
|
||||||
|
@ -62,7 +124,7 @@ func (c clientMock) GetSecret(namespace, name string) (*corev1.Secret, bool, err
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c clientMock) WatchAll(namespaces Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
func (c clientMock) WatchAll(namespaces k8s.Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
||||||
return c.watchChan, nil
|
return c.watchChan, nil
|
||||||
}
|
}
|
||||||
|
|
88
provider/kubernetes/crd/fixtures/services.yml
Normal file
88
provider/kubernetes/crd/fixtures/services.yml
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoami
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
port: 80
|
||||||
|
selector:
|
||||||
|
app: containous
|
||||||
|
task: whoami
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: whoami
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.1
|
||||||
|
- ip: 10.10.0.2
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoami2
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
port: 8080
|
||||||
|
selector:
|
||||||
|
app: containous
|
||||||
|
task: whoami2
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: whoami2
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.3
|
||||||
|
- ip: 10.10.0.4
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
port: 8080
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoamitls
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: web-secure
|
||||||
|
port: 443
|
||||||
|
selector:
|
||||||
|
app: containous
|
||||||
|
task: whoami2
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: whoamitls
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.5
|
||||||
|
- ip: 10.10.0.6
|
||||||
|
ports:
|
||||||
|
- name: web-secure
|
||||||
|
port: 443
|
17
provider/kubernetes/crd/fixtures/simple.yml
Normal file
17
provider/kubernetes/crd/fixtures/simple.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- foo
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/bar`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
17
provider/kubernetes/crd/fixtures/with_bad_host_rule.yml
Normal file
17
provider/kubernetes/crd/fixtures/with_bad_host_rule.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com"0"`) && PathPrefix(`/bar`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
17
provider/kubernetes/crd/fixtures/with_https_default.yml
Normal file
17
provider/kubernetes/crd/fixtures/with_https_default.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- foo
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/bar`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoamitls
|
||||||
|
port: 443
|
44
provider/kubernetes/crd/fixtures/with_middleware.yml
Normal file
44
provider/kubernetes/crd/fixtures/with_middleware.yml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: stripprefix
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- /tobestripped
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: addprefix
|
||||||
|
namespace: foo
|
||||||
|
|
||||||
|
spec:
|
||||||
|
addPrefix:
|
||||||
|
prefix: /tobeadded
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test2.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/tobestripped`)
|
||||||
|
priority: 12
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
||||||
|
middlewares:
|
||||||
|
- name: stripprefix
|
||||||
|
- name: addprefix
|
||||||
|
namespace: foo
|
17
provider/kubernetes/crd/fixtures/with_no_rule_value.yml
Normal file
17
provider/kubernetes/crd/fixtures/with_no_rule_value.yml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: ""
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
31
provider/kubernetes/crd/fixtures/with_tls.yml
Normal file
31
provider/kubernetes/crd/fixtures/with_tls.yml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: supersecret
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
data:
|
||||||
|
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||||
|
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/bar`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
tls:
|
||||||
|
secretName: supersecret
|
23
provider/kubernetes/crd/fixtures/with_two_rules.yml
Normal file
23
provider/kubernetes/crd/fixtures/with_two_rules.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/foo`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/bar`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 14
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
20
provider/kubernetes/crd/fixtures/with_two_services.yml
Normal file
20
provider/kubernetes/crd/fixtures/with_two_services.yml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: Host(`foo.com`) && PathPrefix(`/foo`)
|
||||||
|
kind: Rule
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
||||||
|
- name: whoami2
|
||||||
|
port: 8080
|
||||||
|
|
16
provider/kubernetes/crd/fixtures/with_wrong_rule_kind.yml
Normal file
16
provider/kubernetes/crd/fixtures/with_wrong_rule_kind.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: test.crd
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
|
||||||
|
routes:
|
||||||
|
- match: /prefix
|
||||||
|
priority: 12
|
||||||
|
services:
|
||||||
|
- name: whoami
|
||||||
|
port: 80
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package versioned
|
||||||
|
|
||||||
|
import (
|
||||||
|
traefikv1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1"
|
||||||
|
discovery "k8s.io/client-go/discovery"
|
||||||
|
rest "k8s.io/client-go/rest"
|
||||||
|
flowcontrol "k8s.io/client-go/util/flowcontrol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interface interface {
|
||||||
|
Discovery() discovery.DiscoveryInterface
|
||||||
|
TraefikV1alpha1() traefikv1alpha1.TraefikV1alpha1Interface
|
||||||
|
// Deprecated: please explicitly pick a version if possible.
|
||||||
|
Traefik() traefikv1alpha1.TraefikV1alpha1Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clientset contains the clients for groups. Each group has exactly one
|
||||||
|
// version included in a Clientset.
|
||||||
|
type Clientset struct {
|
||||||
|
*discovery.DiscoveryClient
|
||||||
|
traefikV1alpha1 *traefikv1alpha1.TraefikV1alpha1Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraefikV1alpha1 retrieves the TraefikV1alpha1Client
|
||||||
|
func (c *Clientset) TraefikV1alpha1() traefikv1alpha1.TraefikV1alpha1Interface {
|
||||||
|
return c.traefikV1alpha1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Traefik retrieves the default version of TraefikClient.
|
||||||
|
// Please explicitly pick a version.
|
||||||
|
func (c *Clientset) Traefik() traefikv1alpha1.TraefikV1alpha1Interface {
|
||||||
|
return c.traefikV1alpha1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discovery retrieves the DiscoveryClient
|
||||||
|
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.DiscoveryClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewForConfig creates a new Clientset for the given config.
|
||||||
|
func NewForConfig(c *rest.Config) (*Clientset, error) {
|
||||||
|
configShallowCopy := *c
|
||||||
|
if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
|
||||||
|
configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
|
||||||
|
}
|
||||||
|
var cs Clientset
|
||||||
|
var err error
|
||||||
|
cs.traefikV1alpha1, err = traefikv1alpha1.NewForConfig(&configShallowCopy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewForConfigOrDie creates a new Clientset for the given config and
|
||||||
|
// panics if there is an error in the config.
|
||||||
|
func NewForConfigOrDie(c *rest.Config) *Clientset {
|
||||||
|
var cs Clientset
|
||||||
|
cs.traefikV1alpha1 = traefikv1alpha1.NewForConfigOrDie(c)
|
||||||
|
|
||||||
|
cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
|
||||||
|
return &cs
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Clientset for the given RESTClient.
|
||||||
|
func New(c rest.Interface) *Clientset {
|
||||||
|
var cs Clientset
|
||||||
|
cs.traefikV1alpha1 = traefikv1alpha1.New(c)
|
||||||
|
|
||||||
|
cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
|
||||||
|
return &cs
|
||||||
|
}
|
28
provider/kubernetes/crd/generated/clientset/versioned/doc.go
Normal file
28
provider/kubernetes/crd/generated/clientset/versioned/doc.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
// This package has the automatically generated clientset.
|
||||||
|
package versioned
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
clientset "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned"
|
||||||
|
traefikv1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1"
|
||||||
|
faketraefikv1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
"k8s.io/client-go/discovery"
|
||||||
|
fakediscovery "k8s.io/client-go/discovery/fake"
|
||||||
|
"k8s.io/client-go/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSimpleClientset returns a clientset that will respond with the provided objects.
|
||||||
|
// It's backed by a very simple object tracker that processes creates, updates and deletions as-is,
|
||||||
|
// without applying any validations and/or defaults. It shouldn't be considered a replacement
|
||||||
|
// for a real clientset and is mostly useful in simple unit tests.
|
||||||
|
func NewSimpleClientset(objects ...runtime.Object) *Clientset {
|
||||||
|
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
|
||||||
|
for _, obj := range objects {
|
||||||
|
if err := o.Add(obj); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cs := &Clientset{}
|
||||||
|
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
|
||||||
|
cs.AddReactor("*", "*", testing.ObjectReaction(o))
|
||||||
|
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
|
||||||
|
gvr := action.GetResource()
|
||||||
|
ns := action.GetNamespace()
|
||||||
|
watch, err := o.Watch(gvr, ns)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
return true, watch, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clientset implements clientset.Interface. Meant to be embedded into a
|
||||||
|
// struct to get a default implementation. This makes faking out just the method
|
||||||
|
// you want to test easier.
|
||||||
|
type Clientset struct {
|
||||||
|
testing.Fake
|
||||||
|
discovery *fakediscovery.FakeDiscovery
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
|
||||||
|
return c.discovery
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ clientset.Interface = &Clientset{}
|
||||||
|
|
||||||
|
// TraefikV1alpha1 retrieves the TraefikV1alpha1Client
|
||||||
|
func (c *Clientset) TraefikV1alpha1() traefikv1alpha1.TraefikV1alpha1Interface {
|
||||||
|
return &faketraefikv1alpha1.FakeTraefikV1alpha1{Fake: &c.Fake}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traefik retrieves the TraefikV1alpha1Client
|
||||||
|
func (c *Clientset) Traefik() traefikv1alpha1.TraefikV1alpha1Interface {
|
||||||
|
return &faketraefikv1alpha1.FakeTraefikV1alpha1{Fake: &c.Fake}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
// This package has the automatically generated fake clientset.
|
||||||
|
package fake
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
traefikv1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var scheme = runtime.NewScheme()
|
||||||
|
var codecs = serializer.NewCodecFactory(scheme)
|
||||||
|
var parameterCodec = runtime.NewParameterCodec(scheme)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
|
||||||
|
AddToScheme(scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
|
||||||
|
// of clientsets, like in:
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "k8s.io/client-go/kubernetes"
|
||||||
|
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
|
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||||
|
// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||||
|
//
|
||||||
|
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
|
||||||
|
// correctly.
|
||||||
|
func AddToScheme(scheme *runtime.Scheme) {
|
||||||
|
traefikv1alpha1.AddToScheme(scheme)
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
// This package contains the scheme of the automatically generated clientset.
|
||||||
|
package scheme
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package scheme
|
||||||
|
|
||||||
|
import (
|
||||||
|
traefikv1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Scheme = runtime.NewScheme()
|
||||||
|
var Codecs = serializer.NewCodecFactory(Scheme)
|
||||||
|
var ParameterCodec = runtime.NewParameterCodec(Scheme)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
|
||||||
|
AddToScheme(Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
|
||||||
|
// of clientsets, like in:
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "k8s.io/client-go/kubernetes"
|
||||||
|
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||||
|
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||||
|
// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||||
|
//
|
||||||
|
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
|
||||||
|
// correctly.
|
||||||
|
func AddToScheme(scheme *runtime.Scheme) {
|
||||||
|
traefikv1alpha1.AddToScheme(scheme)
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
// This package has the automatically generated typed clients.
|
||||||
|
package v1alpha1
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
// Package fake has the automatically generated clients.
|
||||||
|
package fake
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
labels "k8s.io/apimachinery/pkg/labels"
|
||||||
|
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
types "k8s.io/apimachinery/pkg/types"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
testing "k8s.io/client-go/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeIngressRoutes implements IngressRouteInterface
|
||||||
|
type FakeIngressRoutes struct {
|
||||||
|
Fake *FakeTraefikV1alpha1
|
||||||
|
ns string
|
||||||
|
}
|
||||||
|
|
||||||
|
var ingressroutesResource = schema.GroupVersionResource{Group: "traefik.containo.us", Version: "v1alpha1", Resource: "ingressroutes"}
|
||||||
|
|
||||||
|
var ingressroutesKind = schema.GroupVersionKind{Group: "traefik.containo.us", Version: "v1alpha1", Kind: "IngressRoute"}
|
||||||
|
|
||||||
|
// Get takes name of the ingressRoute, and returns the corresponding ingressRoute object, and an error if there is any.
|
||||||
|
func (c *FakeIngressRoutes) Get(name string, options v1.GetOptions) (result *v1alpha1.IngressRoute, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewGetAction(ingressroutesResource, c.ns, name), &v1alpha1.IngressRoute{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.IngressRoute), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List takes label and field selectors, and returns the list of IngressRoutes that match those selectors.
|
||||||
|
func (c *FakeIngressRoutes) List(opts v1.ListOptions) (result *v1alpha1.IngressRouteList, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewListAction(ingressroutesResource, ingressroutesKind, c.ns, opts), &v1alpha1.IngressRouteList{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||||
|
if label == nil {
|
||||||
|
label = labels.Everything()
|
||||||
|
}
|
||||||
|
list := &v1alpha1.IngressRouteList{ListMeta: obj.(*v1alpha1.IngressRouteList).ListMeta}
|
||||||
|
for _, item := range obj.(*v1alpha1.IngressRouteList).Items {
|
||||||
|
if label.Matches(labels.Set(item.Labels)) {
|
||||||
|
list.Items = append(list.Items, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch returns a watch.Interface that watches the requested ingressRoutes.
|
||||||
|
func (c *FakeIngressRoutes) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||||
|
return c.Fake.
|
||||||
|
InvokesWatch(testing.NewWatchAction(ingressroutesResource, c.ns, opts))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create takes the representation of a ingressRoute and creates it. Returns the server's representation of the ingressRoute, and an error, if there is any.
|
||||||
|
func (c *FakeIngressRoutes) Create(ingressRoute *v1alpha1.IngressRoute) (result *v1alpha1.IngressRoute, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewCreateAction(ingressroutesResource, c.ns, ingressRoute), &v1alpha1.IngressRoute{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.IngressRoute), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update takes the representation of a ingressRoute and updates it. Returns the server's representation of the ingressRoute, and an error, if there is any.
|
||||||
|
func (c *FakeIngressRoutes) Update(ingressRoute *v1alpha1.IngressRoute) (result *v1alpha1.IngressRoute, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewUpdateAction(ingressroutesResource, c.ns, ingressRoute), &v1alpha1.IngressRoute{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.IngressRoute), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete takes name of the ingressRoute and deletes it. Returns an error if one occurs.
|
||||||
|
func (c *FakeIngressRoutes) Delete(name string, options *v1.DeleteOptions) error {
|
||||||
|
_, err := c.Fake.
|
||||||
|
Invokes(testing.NewDeleteAction(ingressroutesResource, c.ns, name), &v1alpha1.IngressRoute{})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollection deletes a collection of objects.
|
||||||
|
func (c *FakeIngressRoutes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||||
|
action := testing.NewDeleteCollectionAction(ingressroutesResource, c.ns, listOptions)
|
||||||
|
|
||||||
|
_, err := c.Fake.Invokes(action, &v1alpha1.IngressRouteList{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch applies the patch and returns the patched ingressRoute.
|
||||||
|
func (c *FakeIngressRoutes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.IngressRoute, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewPatchSubresourceAction(ingressroutesResource, c.ns, name, data, subresources...), &v1alpha1.IngressRoute{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.IngressRoute), err
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
labels "k8s.io/apimachinery/pkg/labels"
|
||||||
|
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
types "k8s.io/apimachinery/pkg/types"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
testing "k8s.io/client-go/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeMiddlewares implements MiddlewareInterface
|
||||||
|
type FakeMiddlewares struct {
|
||||||
|
Fake *FakeTraefikV1alpha1
|
||||||
|
ns string
|
||||||
|
}
|
||||||
|
|
||||||
|
var middlewaresResource = schema.GroupVersionResource{Group: "traefik.containo.us", Version: "v1alpha1", Resource: "middlewares"}
|
||||||
|
|
||||||
|
var middlewaresKind = schema.GroupVersionKind{Group: "traefik.containo.us", Version: "v1alpha1", Kind: "Middleware"}
|
||||||
|
|
||||||
|
// Get takes name of the middleware, and returns the corresponding middleware object, and an error if there is any.
|
||||||
|
func (c *FakeMiddlewares) Get(name string, options v1.GetOptions) (result *v1alpha1.Middleware, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewGetAction(middlewaresResource, c.ns, name), &v1alpha1.Middleware{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.Middleware), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List takes label and field selectors, and returns the list of Middlewares that match those selectors.
|
||||||
|
func (c *FakeMiddlewares) List(opts v1.ListOptions) (result *v1alpha1.MiddlewareList, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewListAction(middlewaresResource, middlewaresKind, c.ns, opts), &v1alpha1.MiddlewareList{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||||
|
if label == nil {
|
||||||
|
label = labels.Everything()
|
||||||
|
}
|
||||||
|
list := &v1alpha1.MiddlewareList{ListMeta: obj.(*v1alpha1.MiddlewareList).ListMeta}
|
||||||
|
for _, item := range obj.(*v1alpha1.MiddlewareList).Items {
|
||||||
|
if label.Matches(labels.Set(item.Labels)) {
|
||||||
|
list.Items = append(list.Items, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch returns a watch.Interface that watches the requested middlewares.
|
||||||
|
func (c *FakeMiddlewares) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||||
|
return c.Fake.
|
||||||
|
InvokesWatch(testing.NewWatchAction(middlewaresResource, c.ns, opts))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create takes the representation of a middleware and creates it. Returns the server's representation of the middleware, and an error, if there is any.
|
||||||
|
func (c *FakeMiddlewares) Create(middleware *v1alpha1.Middleware) (result *v1alpha1.Middleware, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewCreateAction(middlewaresResource, c.ns, middleware), &v1alpha1.Middleware{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.Middleware), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update takes the representation of a middleware and updates it. Returns the server's representation of the middleware, and an error, if there is any.
|
||||||
|
func (c *FakeMiddlewares) Update(middleware *v1alpha1.Middleware) (result *v1alpha1.Middleware, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewUpdateAction(middlewaresResource, c.ns, middleware), &v1alpha1.Middleware{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.Middleware), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete takes name of the middleware and deletes it. Returns an error if one occurs.
|
||||||
|
func (c *FakeMiddlewares) Delete(name string, options *v1.DeleteOptions) error {
|
||||||
|
_, err := c.Fake.
|
||||||
|
Invokes(testing.NewDeleteAction(middlewaresResource, c.ns, name), &v1alpha1.Middleware{})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollection deletes a collection of objects.
|
||||||
|
func (c *FakeMiddlewares) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||||
|
action := testing.NewDeleteCollectionAction(middlewaresResource, c.ns, listOptions)
|
||||||
|
|
||||||
|
_, err := c.Fake.Invokes(action, &v1alpha1.MiddlewareList{})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch applies the patch and returns the patched middleware.
|
||||||
|
func (c *FakeMiddlewares) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Middleware, err error) {
|
||||||
|
obj, err := c.Fake.
|
||||||
|
Invokes(testing.NewPatchSubresourceAction(middlewaresResource, c.ns, name, data, subresources...), &v1alpha1.Middleware{})
|
||||||
|
|
||||||
|
if obj == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.Middleware), err
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package fake
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1"
|
||||||
|
rest "k8s.io/client-go/rest"
|
||||||
|
testing "k8s.io/client-go/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakeTraefikV1alpha1 struct {
|
||||||
|
*testing.Fake
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeTraefikV1alpha1) IngressRoutes(namespace string) v1alpha1.IngressRouteInterface {
|
||||||
|
return &FakeIngressRoutes{c, namespace}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FakeTraefikV1alpha1) Middlewares(namespace string) v1alpha1.MiddlewareInterface {
|
||||||
|
return &FakeMiddlewares{c, namespace}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESTClient returns a RESTClient that is used to communicate
|
||||||
|
// with API server by this client implementation.
|
||||||
|
func (c *FakeTraefikV1alpha1) RESTClient() rest.Interface {
|
||||||
|
var ret *rest.RESTClient
|
||||||
|
return ret
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
type IngressRouteExpansion interface{}
|
||||||
|
|
||||||
|
type MiddlewareExpansion interface{}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
scheme "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned/scheme"
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
types "k8s.io/apimachinery/pkg/types"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
rest "k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IngressRoutesGetter has a method to return a IngressRouteInterface.
|
||||||
|
// A group's client should implement this interface.
|
||||||
|
type IngressRoutesGetter interface {
|
||||||
|
IngressRoutes(namespace string) IngressRouteInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// IngressRouteInterface has methods to work with IngressRoute resources.
|
||||||
|
type IngressRouteInterface interface {
|
||||||
|
Create(*v1alpha1.IngressRoute) (*v1alpha1.IngressRoute, error)
|
||||||
|
Update(*v1alpha1.IngressRoute) (*v1alpha1.IngressRoute, error)
|
||||||
|
Delete(name string, options *v1.DeleteOptions) error
|
||||||
|
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
|
||||||
|
Get(name string, options v1.GetOptions) (*v1alpha1.IngressRoute, error)
|
||||||
|
List(opts v1.ListOptions) (*v1alpha1.IngressRouteList, error)
|
||||||
|
Watch(opts v1.ListOptions) (watch.Interface, error)
|
||||||
|
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.IngressRoute, err error)
|
||||||
|
IngressRouteExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// ingressRoutes implements IngressRouteInterface
|
||||||
|
type ingressRoutes struct {
|
||||||
|
client rest.Interface
|
||||||
|
ns string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newIngressRoutes returns a IngressRoutes
|
||||||
|
func newIngressRoutes(c *TraefikV1alpha1Client, namespace string) *ingressRoutes {
|
||||||
|
return &ingressRoutes{
|
||||||
|
client: c.RESTClient(),
|
||||||
|
ns: namespace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get takes name of the ingressRoute, and returns the corresponding ingressRoute object, and an error if there is any.
|
||||||
|
func (c *ingressRoutes) Get(name string, options v1.GetOptions) (result *v1alpha1.IngressRoute, err error) {
|
||||||
|
result = &v1alpha1.IngressRoute{}
|
||||||
|
err = c.client.Get().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("ingressroutes").
|
||||||
|
Name(name).
|
||||||
|
VersionedParams(&options, scheme.ParameterCodec).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// List takes label and field selectors, and returns the list of IngressRoutes that match those selectors.
|
||||||
|
func (c *ingressRoutes) List(opts v1.ListOptions) (result *v1alpha1.IngressRouteList, err error) {
|
||||||
|
result = &v1alpha1.IngressRouteList{}
|
||||||
|
err = c.client.Get().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("ingressroutes").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch returns a watch.Interface that watches the requested ingressRoutes.
|
||||||
|
func (c *ingressRoutes) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||||
|
opts.Watch = true
|
||||||
|
return c.client.Get().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("ingressroutes").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Watch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create takes the representation of a ingressRoute and creates it. Returns the server's representation of the ingressRoute, and an error, if there is any.
|
||||||
|
func (c *ingressRoutes) Create(ingressRoute *v1alpha1.IngressRoute) (result *v1alpha1.IngressRoute, err error) {
|
||||||
|
result = &v1alpha1.IngressRoute{}
|
||||||
|
err = c.client.Post().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("ingressroutes").
|
||||||
|
Body(ingressRoute).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update takes the representation of a ingressRoute and updates it. Returns the server's representation of the ingressRoute, and an error, if there is any.
|
||||||
|
func (c *ingressRoutes) Update(ingressRoute *v1alpha1.IngressRoute) (result *v1alpha1.IngressRoute, err error) {
|
||||||
|
result = &v1alpha1.IngressRoute{}
|
||||||
|
err = c.client.Put().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("ingressroutes").
|
||||||
|
Name(ingressRoute.Name).
|
||||||
|
Body(ingressRoute).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete takes name of the ingressRoute and deletes it. Returns an error if one occurs.
|
||||||
|
func (c *ingressRoutes) Delete(name string, options *v1.DeleteOptions) error {
|
||||||
|
return c.client.Delete().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("ingressroutes").
|
||||||
|
Name(name).
|
||||||
|
Body(options).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollection deletes a collection of objects.
|
||||||
|
func (c *ingressRoutes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||||
|
return c.client.Delete().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("ingressroutes").
|
||||||
|
VersionedParams(&listOptions, scheme.ParameterCodec).
|
||||||
|
Body(options).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch applies the patch and returns the patched ingressRoute.
|
||||||
|
func (c *ingressRoutes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.IngressRoute, err error) {
|
||||||
|
result = &v1alpha1.IngressRoute{}
|
||||||
|
err = c.client.Patch(pt).
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("ingressroutes").
|
||||||
|
SubResource(subresources...).
|
||||||
|
Name(name).
|
||||||
|
Body(data).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
scheme "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned/scheme"
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
types "k8s.io/apimachinery/pkg/types"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
rest "k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MiddlewaresGetter has a method to return a MiddlewareInterface.
|
||||||
|
// A group's client should implement this interface.
|
||||||
|
type MiddlewaresGetter interface {
|
||||||
|
Middlewares(namespace string) MiddlewareInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// MiddlewareInterface has methods to work with Middleware resources.
|
||||||
|
type MiddlewareInterface interface {
|
||||||
|
Create(*v1alpha1.Middleware) (*v1alpha1.Middleware, error)
|
||||||
|
Update(*v1alpha1.Middleware) (*v1alpha1.Middleware, error)
|
||||||
|
Delete(name string, options *v1.DeleteOptions) error
|
||||||
|
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
|
||||||
|
Get(name string, options v1.GetOptions) (*v1alpha1.Middleware, error)
|
||||||
|
List(opts v1.ListOptions) (*v1alpha1.MiddlewareList, error)
|
||||||
|
Watch(opts v1.ListOptions) (watch.Interface, error)
|
||||||
|
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Middleware, err error)
|
||||||
|
MiddlewareExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// middlewares implements MiddlewareInterface
|
||||||
|
type middlewares struct {
|
||||||
|
client rest.Interface
|
||||||
|
ns string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newMiddlewares returns a Middlewares
|
||||||
|
func newMiddlewares(c *TraefikV1alpha1Client, namespace string) *middlewares {
|
||||||
|
return &middlewares{
|
||||||
|
client: c.RESTClient(),
|
||||||
|
ns: namespace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get takes name of the middleware, and returns the corresponding middleware object, and an error if there is any.
|
||||||
|
func (c *middlewares) Get(name string, options v1.GetOptions) (result *v1alpha1.Middleware, err error) {
|
||||||
|
result = &v1alpha1.Middleware{}
|
||||||
|
err = c.client.Get().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("middlewares").
|
||||||
|
Name(name).
|
||||||
|
VersionedParams(&options, scheme.ParameterCodec).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// List takes label and field selectors, and returns the list of Middlewares that match those selectors.
|
||||||
|
func (c *middlewares) List(opts v1.ListOptions) (result *v1alpha1.MiddlewareList, err error) {
|
||||||
|
result = &v1alpha1.MiddlewareList{}
|
||||||
|
err = c.client.Get().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("middlewares").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch returns a watch.Interface that watches the requested middlewares.
|
||||||
|
func (c *middlewares) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||||
|
opts.Watch = true
|
||||||
|
return c.client.Get().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("middlewares").
|
||||||
|
VersionedParams(&opts, scheme.ParameterCodec).
|
||||||
|
Watch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create takes the representation of a middleware and creates it. Returns the server's representation of the middleware, and an error, if there is any.
|
||||||
|
func (c *middlewares) Create(middleware *v1alpha1.Middleware) (result *v1alpha1.Middleware, err error) {
|
||||||
|
result = &v1alpha1.Middleware{}
|
||||||
|
err = c.client.Post().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("middlewares").
|
||||||
|
Body(middleware).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update takes the representation of a middleware and updates it. Returns the server's representation of the middleware, and an error, if there is any.
|
||||||
|
func (c *middlewares) Update(middleware *v1alpha1.Middleware) (result *v1alpha1.Middleware, err error) {
|
||||||
|
result = &v1alpha1.Middleware{}
|
||||||
|
err = c.client.Put().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("middlewares").
|
||||||
|
Name(middleware.Name).
|
||||||
|
Body(middleware).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete takes name of the middleware and deletes it. Returns an error if one occurs.
|
||||||
|
func (c *middlewares) Delete(name string, options *v1.DeleteOptions) error {
|
||||||
|
return c.client.Delete().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("middlewares").
|
||||||
|
Name(name).
|
||||||
|
Body(options).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCollection deletes a collection of objects.
|
||||||
|
func (c *middlewares) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||||
|
return c.client.Delete().
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("middlewares").
|
||||||
|
VersionedParams(&listOptions, scheme.ParameterCodec).
|
||||||
|
Body(options).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch applies the patch and returns the patched middleware.
|
||||||
|
func (c *middlewares) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Middleware, err error) {
|
||||||
|
result = &v1alpha1.Middleware{}
|
||||||
|
err = c.client.Patch(pt).
|
||||||
|
Namespace(c.ns).
|
||||||
|
Resource("middlewares").
|
||||||
|
SubResource(subresources...).
|
||||||
|
Name(name).
|
||||||
|
Body(data).
|
||||||
|
Do().
|
||||||
|
Into(result)
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by client-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned/scheme"
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
rest "k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TraefikV1alpha1Interface interface {
|
||||||
|
RESTClient() rest.Interface
|
||||||
|
IngressRoutesGetter
|
||||||
|
MiddlewaresGetter
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraefikV1alpha1Client is used to interact with features provided by the traefik.containo.us group.
|
||||||
|
type TraefikV1alpha1Client struct {
|
||||||
|
restClient rest.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TraefikV1alpha1Client) IngressRoutes(namespace string) IngressRouteInterface {
|
||||||
|
return newIngressRoutes(c, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TraefikV1alpha1Client) Middlewares(namespace string) MiddlewareInterface {
|
||||||
|
return newMiddlewares(c, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewForConfig creates a new TraefikV1alpha1Client for the given config.
|
||||||
|
func NewForConfig(c *rest.Config) (*TraefikV1alpha1Client, error) {
|
||||||
|
config := *c
|
||||||
|
if err := setConfigDefaults(&config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client, err := rest.RESTClientFor(&config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &TraefikV1alpha1Client{client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewForConfigOrDie creates a new TraefikV1alpha1Client for the given config and
|
||||||
|
// panics if there is an error in the config.
|
||||||
|
func NewForConfigOrDie(c *rest.Config) *TraefikV1alpha1Client {
|
||||||
|
client, err := NewForConfig(c)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new TraefikV1alpha1Client for the given RESTClient.
|
||||||
|
func New(c rest.Interface) *TraefikV1alpha1Client {
|
||||||
|
return &TraefikV1alpha1Client{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setConfigDefaults(config *rest.Config) error {
|
||||||
|
gv := v1alpha1.SchemeGroupVersion
|
||||||
|
config.GroupVersion = &gv
|
||||||
|
config.APIPath = "/apis"
|
||||||
|
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
|
||||||
|
|
||||||
|
if config.UserAgent == "" {
|
||||||
|
config.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESTClient returns a RESTClient that is used to communicate
|
||||||
|
// with API server by this client implementation.
|
||||||
|
func (c *TraefikV1alpha1Client) RESTClient() rest.Interface {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.restClient
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package externalversions
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
versioned "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned"
|
||||||
|
internalinterfaces "github.com/containous/traefik/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces"
|
||||||
|
traefik "github.com/containous/traefik/provider/kubernetes/crd/generated/informers/externalversions/traefik"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
cache "k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SharedInformerOption defines the functional option type for SharedInformerFactory.
|
||||||
|
type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory
|
||||||
|
|
||||||
|
type sharedInformerFactory struct {
|
||||||
|
client versioned.Interface
|
||||||
|
namespace string
|
||||||
|
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||||
|
lock sync.Mutex
|
||||||
|
defaultResync time.Duration
|
||||||
|
customResync map[reflect.Type]time.Duration
|
||||||
|
|
||||||
|
informers map[reflect.Type]cache.SharedIndexInformer
|
||||||
|
// startedInformers is used for tracking which informers have been started.
|
||||||
|
// This allows Start() to be called multiple times safely.
|
||||||
|
startedInformers map[reflect.Type]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCustomResyncConfig sets a custom resync period for the specified informer types.
|
||||||
|
func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption {
|
||||||
|
return func(factory *sharedInformerFactory) *sharedInformerFactory {
|
||||||
|
for k, v := range resyncConfig {
|
||||||
|
factory.customResync[reflect.TypeOf(k)] = v
|
||||||
|
}
|
||||||
|
return factory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory.
|
||||||
|
func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption {
|
||||||
|
return func(factory *sharedInformerFactory) *sharedInformerFactory {
|
||||||
|
factory.tweakListOptions = tweakListOptions
|
||||||
|
return factory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNamespace limits the SharedInformerFactory to the specified namespace.
|
||||||
|
func WithNamespace(namespace string) SharedInformerOption {
|
||||||
|
return func(factory *sharedInformerFactory) *sharedInformerFactory {
|
||||||
|
factory.namespace = namespace
|
||||||
|
return factory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces.
|
||||||
|
func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory {
|
||||||
|
return NewSharedInformerFactoryWithOptions(client, defaultResync)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory.
|
||||||
|
// Listers obtained via this SharedInformerFactory will be subject to the same filters
|
||||||
|
// as specified here.
|
||||||
|
// Deprecated: Please use NewSharedInformerFactoryWithOptions instead
|
||||||
|
func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory {
|
||||||
|
return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options.
|
||||||
|
func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory {
|
||||||
|
factory := &sharedInformerFactory{
|
||||||
|
client: client,
|
||||||
|
namespace: v1.NamespaceAll,
|
||||||
|
defaultResync: defaultResync,
|
||||||
|
informers: make(map[reflect.Type]cache.SharedIndexInformer),
|
||||||
|
startedInformers: make(map[reflect.Type]bool),
|
||||||
|
customResync: make(map[reflect.Type]time.Duration),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply all options
|
||||||
|
for _, opt := range options {
|
||||||
|
factory = opt(factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
return factory
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start initializes all requested informers.
|
||||||
|
func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
for informerType, informer := range f.informers {
|
||||||
|
if !f.startedInformers[informerType] {
|
||||||
|
go informer.Run(stopCh)
|
||||||
|
f.startedInformers[informerType] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForCacheSync waits for all started informers' cache were synced.
|
||||||
|
func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
|
||||||
|
informers := func() map[reflect.Type]cache.SharedIndexInformer {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
informers := map[reflect.Type]cache.SharedIndexInformer{}
|
||||||
|
for informerType, informer := range f.informers {
|
||||||
|
if f.startedInformers[informerType] {
|
||||||
|
informers[informerType] = informer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return informers
|
||||||
|
}()
|
||||||
|
|
||||||
|
res := map[reflect.Type]bool{}
|
||||||
|
for informType, informer := range informers {
|
||||||
|
res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalInformerFor returns the SharedIndexInformer for obj using an internal
|
||||||
|
// client.
|
||||||
|
func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
informerType := reflect.TypeOf(obj)
|
||||||
|
informer, exists := f.informers[informerType]
|
||||||
|
if exists {
|
||||||
|
return informer
|
||||||
|
}
|
||||||
|
|
||||||
|
resyncPeriod, exists := f.customResync[informerType]
|
||||||
|
if !exists {
|
||||||
|
resyncPeriod = f.defaultResync
|
||||||
|
}
|
||||||
|
|
||||||
|
informer = newFunc(f.client, resyncPeriod)
|
||||||
|
f.informers[informerType] = informer
|
||||||
|
|
||||||
|
return informer
|
||||||
|
}
|
||||||
|
|
||||||
|
// SharedInformerFactory provides shared informers for resources in all known
|
||||||
|
// API group versions.
|
||||||
|
type SharedInformerFactory interface {
|
||||||
|
internalinterfaces.SharedInformerFactory
|
||||||
|
ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
|
||||||
|
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
|
||||||
|
|
||||||
|
Traefik() traefik.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *sharedInformerFactory) Traefik() traefik.Interface {
|
||||||
|
return traefik.New(f, f.namespace, f.tweakListOptions)
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package externalversions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
cache "k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenericInformer is type of SharedIndexInformer which will locate and delegate to other
|
||||||
|
// sharedInformers based on type
|
||||||
|
type GenericInformer interface {
|
||||||
|
Informer() cache.SharedIndexInformer
|
||||||
|
Lister() cache.GenericLister
|
||||||
|
}
|
||||||
|
|
||||||
|
type genericInformer struct {
|
||||||
|
informer cache.SharedIndexInformer
|
||||||
|
resource schema.GroupResource
|
||||||
|
}
|
||||||
|
|
||||||
|
// Informer returns the SharedIndexInformer.
|
||||||
|
func (f *genericInformer) Informer() cache.SharedIndexInformer {
|
||||||
|
return f.informer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lister returns the GenericLister.
|
||||||
|
func (f *genericInformer) Lister() cache.GenericLister {
|
||||||
|
return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForResource gives generic access to a shared informer of the matching type
|
||||||
|
// TODO extend this to unknown resources with a client pool
|
||||||
|
func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) {
|
||||||
|
switch resource {
|
||||||
|
case v1alpha1.SchemeGroupVersion.WithResource("ingressroutes"):
|
||||||
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Traefik().V1alpha1().IngressRoutes().Informer()}, nil
|
||||||
|
case v1alpha1.SchemeGroupVersion.WithResource("middlewares"):
|
||||||
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Traefik().V1alpha1().Middlewares().Informer()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no informer found for %v", resource)
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package internalinterfaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
versioned "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
cache "k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
|
||||||
|
|
||||||
|
// SharedInformerFactory a small interface to allow for adding an informer without an import cycle
|
||||||
|
type SharedInformerFactory interface {
|
||||||
|
Start(stopCh <-chan struct{})
|
||||||
|
InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
|
||||||
|
}
|
||||||
|
|
||||||
|
type TweakListOptionsFunc func(*v1.ListOptions)
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package traefik
|
||||||
|
|
||||||
|
import (
|
||||||
|
internalinterfaces "github.com/containous/traefik/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces"
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/generated/informers/externalversions/traefik/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface provides access to each of this group's versions.
|
||||||
|
type Interface interface {
|
||||||
|
// V1alpha1 provides access to shared informers for resources in V1alpha1.
|
||||||
|
V1alpha1() v1alpha1.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
type group struct {
|
||||||
|
factory internalinterfaces.SharedInformerFactory
|
||||||
|
namespace string
|
||||||
|
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Interface.
|
||||||
|
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
|
||||||
|
return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
|
||||||
|
}
|
||||||
|
|
||||||
|
// V1alpha1 returns a new v1alpha1.Interface.
|
||||||
|
func (g *group) V1alpha1() v1alpha1.Interface {
|
||||||
|
return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
versioned "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned"
|
||||||
|
internalinterfaces "github.com/containous/traefik/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces"
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/generated/listers/traefik/v1alpha1"
|
||||||
|
traefikv1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
cache "k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IngressRouteInformer provides access to a shared informer and lister for
|
||||||
|
// IngressRoutes.
|
||||||
|
type IngressRouteInformer interface {
|
||||||
|
Informer() cache.SharedIndexInformer
|
||||||
|
Lister() v1alpha1.IngressRouteLister
|
||||||
|
}
|
||||||
|
|
||||||
|
type ingressRouteInformer struct {
|
||||||
|
factory internalinterfaces.SharedInformerFactory
|
||||||
|
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIngressRouteInformer constructs a new informer for IngressRoute type.
|
||||||
|
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||||
|
// one. This reduces memory footprint and number of connections to the server.
|
||||||
|
func NewIngressRouteInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
|
||||||
|
return NewFilteredIngressRouteInformer(client, namespace, resyncPeriod, indexers, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilteredIngressRouteInformer constructs a new informer for IngressRoute type.
|
||||||
|
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||||
|
// one. This reduces memory footprint and number of connections to the server.
|
||||||
|
func NewFilteredIngressRouteInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
|
||||||
|
return cache.NewSharedIndexInformer(
|
||||||
|
&cache.ListWatch{
|
||||||
|
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
|
||||||
|
if tweakListOptions != nil {
|
||||||
|
tweakListOptions(&options)
|
||||||
|
}
|
||||||
|
return client.TraefikV1alpha1().IngressRoutes(namespace).List(options)
|
||||||
|
},
|
||||||
|
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||||
|
if tweakListOptions != nil {
|
||||||
|
tweakListOptions(&options)
|
||||||
|
}
|
||||||
|
return client.TraefikV1alpha1().IngressRoutes(namespace).Watch(options)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&traefikv1alpha1.IngressRoute{},
|
||||||
|
resyncPeriod,
|
||||||
|
indexers,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ingressRouteInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
|
||||||
|
return NewFilteredIngressRouteInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ingressRouteInformer) Informer() cache.SharedIndexInformer {
|
||||||
|
return f.factory.InformerFor(&traefikv1alpha1.IngressRoute{}, f.defaultInformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *ingressRouteInformer) Lister() v1alpha1.IngressRouteLister {
|
||||||
|
return v1alpha1.NewIngressRouteLister(f.Informer().GetIndexer())
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
internalinterfaces "github.com/containous/traefik/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface provides access to all the informers in this group version.
|
||||||
|
type Interface interface {
|
||||||
|
// IngressRoutes returns a IngressRouteInformer.
|
||||||
|
IngressRoutes() IngressRouteInformer
|
||||||
|
// Middlewares returns a MiddlewareInformer.
|
||||||
|
Middlewares() MiddlewareInformer
|
||||||
|
}
|
||||||
|
|
||||||
|
type version struct {
|
||||||
|
factory internalinterfaces.SharedInformerFactory
|
||||||
|
namespace string
|
||||||
|
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Interface.
|
||||||
|
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
|
||||||
|
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IngressRoutes returns a IngressRouteInformer.
|
||||||
|
func (v *version) IngressRoutes() IngressRouteInformer {
|
||||||
|
return &ingressRouteInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middlewares returns a MiddlewareInformer.
|
||||||
|
func (v *version) Middlewares() MiddlewareInformer {
|
||||||
|
return &middlewareInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by informer-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
versioned "github.com/containous/traefik/provider/kubernetes/crd/generated/clientset/versioned"
|
||||||
|
internalinterfaces "github.com/containous/traefik/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces"
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/generated/listers/traefik/v1alpha1"
|
||||||
|
traefikv1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
watch "k8s.io/apimachinery/pkg/watch"
|
||||||
|
cache "k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MiddlewareInformer provides access to a shared informer and lister for
|
||||||
|
// Middlewares.
|
||||||
|
type MiddlewareInformer interface {
|
||||||
|
Informer() cache.SharedIndexInformer
|
||||||
|
Lister() v1alpha1.MiddlewareLister
|
||||||
|
}
|
||||||
|
|
||||||
|
type middlewareInformer struct {
|
||||||
|
factory internalinterfaces.SharedInformerFactory
|
||||||
|
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMiddlewareInformer constructs a new informer for Middleware type.
|
||||||
|
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||||
|
// one. This reduces memory footprint and number of connections to the server.
|
||||||
|
func NewMiddlewareInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
|
||||||
|
return NewFilteredMiddlewareInformer(client, namespace, resyncPeriod, indexers, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFilteredMiddlewareInformer constructs a new informer for Middleware type.
|
||||||
|
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||||
|
// one. This reduces memory footprint and number of connections to the server.
|
||||||
|
func NewFilteredMiddlewareInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
|
||||||
|
return cache.NewSharedIndexInformer(
|
||||||
|
&cache.ListWatch{
|
||||||
|
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
|
||||||
|
if tweakListOptions != nil {
|
||||||
|
tweakListOptions(&options)
|
||||||
|
}
|
||||||
|
return client.TraefikV1alpha1().Middlewares(namespace).List(options)
|
||||||
|
},
|
||||||
|
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||||
|
if tweakListOptions != nil {
|
||||||
|
tweakListOptions(&options)
|
||||||
|
}
|
||||||
|
return client.TraefikV1alpha1().Middlewares(namespace).Watch(options)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&traefikv1alpha1.Middleware{},
|
||||||
|
resyncPeriod,
|
||||||
|
indexers,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *middlewareInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
|
||||||
|
return NewFilteredMiddlewareInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *middlewareInformer) Informer() cache.SharedIndexInformer {
|
||||||
|
return f.factory.InformerFor(&traefikv1alpha1.Middleware{}, f.defaultInformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *middlewareInformer) Lister() v1alpha1.MiddlewareLister {
|
||||||
|
return v1alpha1.NewMiddlewareLister(f.Informer().GetIndexer())
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by lister-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
// IngressRouteListerExpansion allows custom methods to be added to
|
||||||
|
// IngressRouteLister.
|
||||||
|
type IngressRouteListerExpansion interface{}
|
||||||
|
|
||||||
|
// IngressRouteNamespaceListerExpansion allows custom methods to be added to
|
||||||
|
// IngressRouteNamespaceLister.
|
||||||
|
type IngressRouteNamespaceListerExpansion interface{}
|
||||||
|
|
||||||
|
// MiddlewareListerExpansion allows custom methods to be added to
|
||||||
|
// MiddlewareLister.
|
||||||
|
type MiddlewareListerExpansion interface{}
|
||||||
|
|
||||||
|
// MiddlewareNamespaceListerExpansion allows custom methods to be added to
|
||||||
|
// MiddlewareNamespaceLister.
|
||||||
|
type MiddlewareNamespaceListerExpansion interface{}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by lister-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IngressRouteLister helps list IngressRoutes.
|
||||||
|
type IngressRouteLister interface {
|
||||||
|
// List lists all IngressRoutes in the indexer.
|
||||||
|
List(selector labels.Selector) (ret []*v1alpha1.IngressRoute, err error)
|
||||||
|
// IngressRoutes returns an object that can list and get IngressRoutes.
|
||||||
|
IngressRoutes(namespace string) IngressRouteNamespaceLister
|
||||||
|
IngressRouteListerExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// ingressRouteLister implements the IngressRouteLister interface.
|
||||||
|
type ingressRouteLister struct {
|
||||||
|
indexer cache.Indexer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIngressRouteLister returns a new IngressRouteLister.
|
||||||
|
func NewIngressRouteLister(indexer cache.Indexer) IngressRouteLister {
|
||||||
|
return &ingressRouteLister{indexer: indexer}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List lists all IngressRoutes in the indexer.
|
||||||
|
func (s *ingressRouteLister) List(selector labels.Selector) (ret []*v1alpha1.IngressRoute, err error) {
|
||||||
|
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
|
||||||
|
ret = append(ret, m.(*v1alpha1.IngressRoute))
|
||||||
|
})
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IngressRoutes returns an object that can list and get IngressRoutes.
|
||||||
|
func (s *ingressRouteLister) IngressRoutes(namespace string) IngressRouteNamespaceLister {
|
||||||
|
return ingressRouteNamespaceLister{indexer: s.indexer, namespace: namespace}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IngressRouteNamespaceLister helps list and get IngressRoutes.
|
||||||
|
type IngressRouteNamespaceLister interface {
|
||||||
|
// List lists all IngressRoutes in the indexer for a given namespace.
|
||||||
|
List(selector labels.Selector) (ret []*v1alpha1.IngressRoute, err error)
|
||||||
|
// Get retrieves the IngressRoute from the indexer for a given namespace and name.
|
||||||
|
Get(name string) (*v1alpha1.IngressRoute, error)
|
||||||
|
IngressRouteNamespaceListerExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// ingressRouteNamespaceLister implements the IngressRouteNamespaceLister
|
||||||
|
// interface.
|
||||||
|
type ingressRouteNamespaceLister struct {
|
||||||
|
indexer cache.Indexer
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// List lists all IngressRoutes in the indexer for a given namespace.
|
||||||
|
func (s ingressRouteNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.IngressRoute, err error) {
|
||||||
|
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
|
||||||
|
ret = append(ret, m.(*v1alpha1.IngressRoute))
|
||||||
|
})
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the IngressRoute from the indexer for a given namespace and name.
|
||||||
|
func (s ingressRouteNamespaceLister) Get(name string) (*v1alpha1.IngressRoute, error) {
|
||||||
|
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.NewNotFound(v1alpha1.Resource("ingressroute"), name)
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.IngressRoute), nil
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by lister-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1alpha1 "github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MiddlewareLister helps list Middlewares.
|
||||||
|
type MiddlewareLister interface {
|
||||||
|
// List lists all Middlewares in the indexer.
|
||||||
|
List(selector labels.Selector) (ret []*v1alpha1.Middleware, err error)
|
||||||
|
// Middlewares returns an object that can list and get Middlewares.
|
||||||
|
Middlewares(namespace string) MiddlewareNamespaceLister
|
||||||
|
MiddlewareListerExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// middlewareLister implements the MiddlewareLister interface.
|
||||||
|
type middlewareLister struct {
|
||||||
|
indexer cache.Indexer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMiddlewareLister returns a new MiddlewareLister.
|
||||||
|
func NewMiddlewareLister(indexer cache.Indexer) MiddlewareLister {
|
||||||
|
return &middlewareLister{indexer: indexer}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List lists all Middlewares in the indexer.
|
||||||
|
func (s *middlewareLister) List(selector labels.Selector) (ret []*v1alpha1.Middleware, err error) {
|
||||||
|
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
|
||||||
|
ret = append(ret, m.(*v1alpha1.Middleware))
|
||||||
|
})
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middlewares returns an object that can list and get Middlewares.
|
||||||
|
func (s *middlewareLister) Middlewares(namespace string) MiddlewareNamespaceLister {
|
||||||
|
return middlewareNamespaceLister{indexer: s.indexer, namespace: namespace}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MiddlewareNamespaceLister helps list and get Middlewares.
|
||||||
|
type MiddlewareNamespaceLister interface {
|
||||||
|
// List lists all Middlewares in the indexer for a given namespace.
|
||||||
|
List(selector labels.Selector) (ret []*v1alpha1.Middleware, err error)
|
||||||
|
// Get retrieves the Middleware from the indexer for a given namespace and name.
|
||||||
|
Get(name string) (*v1alpha1.Middleware, error)
|
||||||
|
MiddlewareNamespaceListerExpansion
|
||||||
|
}
|
||||||
|
|
||||||
|
// middlewareNamespaceLister implements the MiddlewareNamespaceLister
|
||||||
|
// interface.
|
||||||
|
type middlewareNamespaceLister struct {
|
||||||
|
indexer cache.Indexer
|
||||||
|
namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// List lists all Middlewares in the indexer for a given namespace.
|
||||||
|
func (s middlewareNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Middleware, err error) {
|
||||||
|
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
|
||||||
|
ret = append(ret, m.(*v1alpha1.Middleware))
|
||||||
|
})
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the Middleware from the indexer for a given namespace and name.
|
||||||
|
func (s middlewareNamespaceLister) Get(name string) (*v1alpha1.Middleware, error) {
|
||||||
|
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.NewNotFound(v1alpha1.Resource("middleware"), name)
|
||||||
|
}
|
||||||
|
return obj.(*v1alpha1.Middleware), nil
|
||||||
|
}
|
458
provider/kubernetes/crd/kubernetes.go
Normal file
458
provider/kubernetes/crd/kubernetes.go
Normal file
|
@ -0,0 +1,458 @@
|
||||||
|
package crd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cenkalti/backoff"
|
||||||
|
"github.com/containous/traefik/config"
|
||||||
|
"github.com/containous/traefik/job"
|
||||||
|
"github.com/containous/traefik/log"
|
||||||
|
"github.com/containous/traefik/provider"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/crd/traefik/v1alpha1"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/k8s"
|
||||||
|
"github.com/containous/traefik/safe"
|
||||||
|
"github.com/containous/traefik/tls"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
|
||||||
|
traefikDefaultIngressClass = "traefik"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IngressEndpoint holds the endpoint information for the Kubernetes provider.
|
||||||
|
type IngressEndpoint struct {
|
||||||
|
IP string `description:"IP used for Kubernetes Ingress endpoints"`
|
||||||
|
Hostname string `description:"Hostname used for Kubernetes Ingress endpoints"`
|
||||||
|
PublishedService string `description:"Published Kubernetes Service to copy status from"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider holds configurations of the provider.
|
||||||
|
type Provider struct {
|
||||||
|
provider.BaseProvider `mapstructure:",squash" export:"true"`
|
||||||
|
Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)"`
|
||||||
|
Token string `description:"Kubernetes bearer token (not needed for in-cluster client)"`
|
||||||
|
CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)"`
|
||||||
|
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers" export:"true"`
|
||||||
|
EnablePassTLSCert bool `description:"Kubernetes enable Pass TLS Client Certs" export:"true"` // Deprecated
|
||||||
|
Namespaces k8s.Namespaces `description:"Kubernetes namespaces" export:"true"`
|
||||||
|
LabelSelector string `description:"Kubernetes Ingress label selector to use" export:"true"`
|
||||||
|
IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for" export:"true"`
|
||||||
|
IngressEndpoint *IngressEndpoint `description:"Kubernetes Ingress Endpoint"`
|
||||||
|
lastConfiguration safe.Safe
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provider) newK8sClient(ctx context.Context, ingressLabelSelector string) (*clientWrapper, error) {
|
||||||
|
ingLabelSel, err := labels.Parse(ingressLabelSelector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid ingress label selector: %q", ingressLabelSelector)
|
||||||
|
}
|
||||||
|
log.FromContext(ctx).Infof("ingress label selector is: %q", ingLabelSel)
|
||||||
|
|
||||||
|
withEndpoint := ""
|
||||||
|
if p.Endpoint != "" {
|
||||||
|
withEndpoint = fmt.Sprintf(" with endpoint %v", p.Endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
var client *clientWrapper
|
||||||
|
switch {
|
||||||
|
case os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "":
|
||||||
|
log.FromContext(ctx).Infof("Creating in-cluster Provider client%s", withEndpoint)
|
||||||
|
client, err = newInClusterClient(p.Endpoint)
|
||||||
|
case os.Getenv("KUBECONFIG") != "":
|
||||||
|
log.FromContext(ctx).Infof("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG"))
|
||||||
|
client, err = newExternalClusterClientFromFile(os.Getenv("KUBECONFIG"))
|
||||||
|
default:
|
||||||
|
log.FromContext(ctx).Infof("Creating cluster-external Provider client%s", withEndpoint)
|
||||||
|
client, err = newExternalClusterClient(p.Endpoint, p.Token, p.CertAuthFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
client.ingressLabelSelector = ingLabelSel
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init the provider.
|
||||||
|
func (p *Provider) Init() error {
|
||||||
|
return p.BaseProvider.Init()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide allows the k8s provider to provide configurations to traefik
|
||||||
|
// using the given configuration channel.
|
||||||
|
func (p *Provider) Provide(configurationChan chan<- config.Message, pool *safe.Pool) error {
|
||||||
|
ctxLog := log.With(context.Background(), log.Str(log.ProviderName, "kubernetescrd"))
|
||||||
|
logger := log.FromContext(ctxLog)
|
||||||
|
// Tell glog (used by client-go) to log into STDERR. Otherwise, we risk
|
||||||
|
// certain kinds of API errors getting logged into a directory not
|
||||||
|
// available in a `FROM scratch` Docker container, causing glog to abort
|
||||||
|
// hard with an exit code > 0.
|
||||||
|
err := flag.Set("logtostderr", "true")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debugf("Using Ingress label selector: %q", p.LabelSelector)
|
||||||
|
k8sClient, err := p.newK8sClient(ctxLog, p.LabelSelector)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.Go(func(stop chan bool) {
|
||||||
|
operation := func() error {
|
||||||
|
stopWatch := make(chan struct{}, 1)
|
||||||
|
defer close(stopWatch)
|
||||||
|
eventsChan, err := k8sClient.WatchAll(p.Namespaces, stopWatch)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Error watching kubernetes events: %v", err)
|
||||||
|
timer := time.NewTimer(1 * time.Second)
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
return err
|
||||||
|
case <-stop:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
return nil
|
||||||
|
case event := <-eventsChan:
|
||||||
|
conf := p.loadConfigurationFromIngresses(ctxLog, k8sClient)
|
||||||
|
|
||||||
|
if reflect.DeepEqual(p.lastConfiguration.Get(), conf) {
|
||||||
|
logger.Debugf("Skipping Kubernetes event kind %T", event)
|
||||||
|
} else {
|
||||||
|
p.lastConfiguration.Set(conf)
|
||||||
|
configurationChan <- config.Message{
|
||||||
|
ProviderName: "kubernetescrd",
|
||||||
|
Configuration: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notify := func(err error, time time.Duration) {
|
||||||
|
logger.Errorf("Provider connection error: %s; retrying in %s", err, time)
|
||||||
|
}
|
||||||
|
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Cannot connect to Provider: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkStringQuoteValidity(value string) error {
|
||||||
|
_, err := strconv.Unquote(`"` + value + `"`)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadServers(client Client, namespace string, svc v1alpha1.Service) ([]config.Server, error) {
|
||||||
|
strategy := svc.Strategy
|
||||||
|
if strategy == "" {
|
||||||
|
strategy = "RoundRobin"
|
||||||
|
}
|
||||||
|
if strategy != "RoundRobin" {
|
||||||
|
return nil, fmt.Errorf("load balancing strategy %v is not supported", strategy)
|
||||||
|
}
|
||||||
|
|
||||||
|
service, exists, err := client.GetService(namespace, svc.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return nil, errors.New("service not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var portSpec corev1.ServicePort
|
||||||
|
var match bool
|
||||||
|
// TODO: support name ports? do we actually care?
|
||||||
|
for _, p := range service.Spec.Ports {
|
||||||
|
if svc.Port == p.Port {
|
||||||
|
portSpec = p
|
||||||
|
match = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !match {
|
||||||
|
return nil, errors.New("service port not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var servers []config.Server
|
||||||
|
if service.Spec.Type == corev1.ServiceTypeExternalName {
|
||||||
|
servers = append(servers, config.Server{
|
||||||
|
URL: fmt.Sprintf("http://%s:%d", service.Spec.ExternalName, portSpec.Port),
|
||||||
|
Weight: 1,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
|
||||||
|
if endpointsErr != nil {
|
||||||
|
return nil, endpointsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if !endpointsExists {
|
||||||
|
return nil, errors.New("endpoints not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(endpoints.Subsets) == 0 {
|
||||||
|
return nil, errors.New("subset not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var port int32
|
||||||
|
for _, subset := range endpoints.Subsets {
|
||||||
|
for _, p := range subset.Ports {
|
||||||
|
if portSpec.Name == p.Name {
|
||||||
|
port = p.Port
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if port == 0 {
|
||||||
|
return nil, errors.New("cannot define a port")
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol := "http"
|
||||||
|
if port == 443 || strings.HasPrefix(portSpec.Name, "https") {
|
||||||
|
protocol = "https"
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range subset.Addresses {
|
||||||
|
servers = append(servers, config.Server{
|
||||||
|
URL: fmt.Sprintf("%s://%s:%d", protocol, addr.IP, port),
|
||||||
|
Weight: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Client) *config.Configuration {
|
||||||
|
|
||||||
|
conf := &config.Configuration{
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{},
|
||||||
|
},
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
}
|
||||||
|
tlsConfigs := make(map[string]*tls.Configuration)
|
||||||
|
|
||||||
|
for _, ingressRoute := range client.GetIngressRoutes() {
|
||||||
|
logger := log.FromContext(log.With(ctx, log.Str("ingress", ingressRoute.Name), log.Str("namespace", ingressRoute.Namespace)))
|
||||||
|
|
||||||
|
// TODO keep the name ingressClass?
|
||||||
|
if !shouldProcessIngress(p.IngressClass, ingressRoute.Annotations[annotationKubernetesIngressClass]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := getTLS(ctx, ingressRoute, client, tlsConfigs)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Error configuring TLS: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ingressName := ingressRoute.Name
|
||||||
|
if len(ingressName) == 0 {
|
||||||
|
ingressName = ingressRoute.GenerateName
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, route := range ingressRoute.Spec.Routes {
|
||||||
|
if route.Kind != "Rule" {
|
||||||
|
logger.Errorf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(route.Match) == 0 {
|
||||||
|
logger.Errorf("Empty match rule")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkStringQuoteValidity(route.Match); err != nil {
|
||||||
|
logger.Errorf("Invalid syntax for match rule: %s", route.Match)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var allServers []config.Server
|
||||||
|
for _, service := range route.Services {
|
||||||
|
servers, err := loadServers(client, ingressRoute.Namespace, service)
|
||||||
|
if err != nil {
|
||||||
|
logger.
|
||||||
|
WithField("serviceName", service.Name).
|
||||||
|
WithField("servicePort", service.Port).
|
||||||
|
Errorf("Cannot create service: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
allServers = append(allServers, servers...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: support middlewares from other providers.
|
||||||
|
// Mechanism: in the spec, prefix the name with the provider name,
|
||||||
|
// with dot as the separator. In which case. we ignore the
|
||||||
|
// namespace.
|
||||||
|
|
||||||
|
var mds []string
|
||||||
|
for _, mi := range route.Middlewares {
|
||||||
|
ns := mi.Namespace
|
||||||
|
if len(ns) == 0 {
|
||||||
|
ns = ingressRoute.Namespace
|
||||||
|
}
|
||||||
|
mds = append(mds, makeID(ns, mi.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
_, err = h.Write([]byte(route.Match))
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := fmt.Sprintf("%s-%.10x", ingressName, h.Sum(nil))
|
||||||
|
|
||||||
|
serviceName := makeID(ingressRoute.Namespace, key)
|
||||||
|
|
||||||
|
conf.HTTP.Routers[serviceName] = &config.Router{
|
||||||
|
Middlewares: mds,
|
||||||
|
Priority: route.Priority,
|
||||||
|
EntryPoints: ingressRoute.Spec.EntryPoints,
|
||||||
|
Rule: route.Match,
|
||||||
|
Service: serviceName,
|
||||||
|
}
|
||||||
|
if ingressRoute.Spec.TLS != nil {
|
||||||
|
conf.HTTP.Routers[serviceName].TLS = &config.RouterTLSConfig{}
|
||||||
|
}
|
||||||
|
conf.HTTP.Services[serviceName] = &config.Service{
|
||||||
|
LoadBalancer: &config.LoadBalancerService{
|
||||||
|
Servers: allServers,
|
||||||
|
// TODO: support other strategies.
|
||||||
|
Method: "wrr",
|
||||||
|
PassHostHeader: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.TLS = getTLSConfig(tlsConfigs)
|
||||||
|
|
||||||
|
for _, middleware := range client.GetMiddlewares() {
|
||||||
|
conf.HTTP.Middlewares[makeID(middleware.Namespace, middleware.Name)] = &middleware.Spec
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeID(namespace, name string) string {
|
||||||
|
if namespace == "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return namespace + "/" + name
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldProcessIngress(ingressClass string, ingressClassAnnotation string) bool {
|
||||||
|
return ingressClass == ingressClassAnnotation ||
|
||||||
|
(len(ingressClass) == 0 && ingressClassAnnotation == traefikDefaultIngressClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTLS(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, k8sClient Client, tlsConfigs map[string]*tls.Configuration) error {
|
||||||
|
if ingressRoute.Spec.TLS == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ingressRoute.Spec.TLS.SecretName == "" {
|
||||||
|
log.FromContext(ctx).Debugf("Skipping TLS sub-section: No secret name provided")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
configKey := ingressRoute.Namespace + "/" + ingressRoute.Spec.TLS.SecretName
|
||||||
|
if _, tlsExists := tlsConfigs[configKey]; !tlsExists {
|
||||||
|
secret, exists, err := k8sClient.GetSecret(ingressRoute.Namespace, ingressRoute.Spec.TLS.SecretName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to fetch secret %s/%s: %v", ingressRoute.Namespace, ingressRoute.Spec.TLS.SecretName, err)
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("secret %s/%s does not exist", ingressRoute.Namespace, ingressRoute.Spec.TLS.SecretName)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, key, err := getCertificateBlocks(secret, ingressRoute.Namespace, ingressRoute.Spec.TLS.SecretName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfigs[configKey] = &tls.Configuration{
|
||||||
|
Certificate: &tls.Certificate{
|
||||||
|
CertFile: tls.FileOrContent(cert),
|
||||||
|
KeyFile: tls.FileOrContent(key),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTLSConfig(tlsConfigs map[string]*tls.Configuration) []*tls.Configuration {
|
||||||
|
var secretNames []string
|
||||||
|
for secretName := range tlsConfigs {
|
||||||
|
secretNames = append(secretNames, secretName)
|
||||||
|
}
|
||||||
|
sort.Strings(secretNames)
|
||||||
|
|
||||||
|
var configs []*tls.Configuration
|
||||||
|
for _, secretName := range secretNames {
|
||||||
|
configs = append(configs, tlsConfigs[secretName])
|
||||||
|
}
|
||||||
|
|
||||||
|
return configs
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCertificateBlocks(secret *corev1.Secret, namespace, secretName string) (string, string, error) {
|
||||||
|
var missingEntries []string
|
||||||
|
|
||||||
|
tlsCrtData, tlsCrtExists := secret.Data["tls.crt"]
|
||||||
|
if !tlsCrtExists {
|
||||||
|
missingEntries = append(missingEntries, "tls.crt")
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsKeyData, tlsKeyExists := secret.Data["tls.key"]
|
||||||
|
if !tlsKeyExists {
|
||||||
|
missingEntries = append(missingEntries, "tls.key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(missingEntries) > 0 {
|
||||||
|
return "", "", fmt.Errorf("secret %s/%s is missing the following TLS data entries: %s",
|
||||||
|
namespace, secretName, strings.Join(missingEntries, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
cert := string(tlsCrtData)
|
||||||
|
if cert == "" {
|
||||||
|
missingEntries = append(missingEntries, "tls.crt")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := string(tlsKeyData)
|
||||||
|
if key == "" {
|
||||||
|
missingEntries = append(missingEntries, "tls.key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(missingEntries) > 0 {
|
||||||
|
return "", "", fmt.Errorf("secret %s/%s contains the following empty TLS data entries: %s",
|
||||||
|
namespace, secretName, strings.Join(missingEntries, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert, key, nil
|
||||||
|
}
|
368
provider/kubernetes/crd/kubernetes_test.go
Normal file
368
provider/kubernetes/crd/kubernetes_test.go
Normal file
|
@ -0,0 +1,368 @@
|
||||||
|
package crd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/config"
|
||||||
|
"github.com/containous/traefik/provider"
|
||||||
|
"github.com/containous/traefik/tls"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ provider.Provider = (*Provider)(nil)
|
||||||
|
|
||||||
|
func TestLoadIngressRoutes(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
ingressClass string
|
||||||
|
paths []string
|
||||||
|
expected *config.Configuration
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "Empty",
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Simple Ingress Route, with foo entrypoint",
|
||||||
|
paths: []string{"services.yml", "simple.yml"},
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{
|
||||||
|
"default/test.crd-6b204d94623b3df4370c": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default/test.crd-6b204d94623b3df4370c",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
|
||||||
|
Priority: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{
|
||||||
|
"default/test.crd-6b204d94623b3df4370c": {
|
||||||
|
LoadBalancer: &config.LoadBalancerService{
|
||||||
|
Servers: []config.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Method: "wrr",
|
||||||
|
PassHostHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Simple Ingress Route with middleware",
|
||||||
|
paths: []string{"services.yml", "with_middleware.yml"},
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{
|
||||||
|
"default/test2.crd-23c7f4c450289ee29016": {
|
||||||
|
EntryPoints: []string{"web"},
|
||||||
|
Service: "default/test2.crd-23c7f4c450289ee29016",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/tobestripped`)",
|
||||||
|
Priority: 12,
|
||||||
|
Middlewares: []string{"default/stripprefix", "foo/addprefix"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*config.Middleware{
|
||||||
|
"default/stripprefix": {
|
||||||
|
StripPrefix: &config.StripPrefix{
|
||||||
|
Prefixes: []string{"/tobestripped"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"foo/addprefix": {
|
||||||
|
AddPrefix: &config.AddPrefix{
|
||||||
|
Prefix: "/tobeadded",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: map[string]*config.Service{
|
||||||
|
"default/test2.crd-23c7f4c450289ee29016": {
|
||||||
|
LoadBalancer: &config.LoadBalancerService{
|
||||||
|
Servers: []config.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Method: "wrr",
|
||||||
|
PassHostHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "One ingress Route with two different rules",
|
||||||
|
paths: []string{"services.yml", "with_two_rules.yml"},
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{
|
||||||
|
"default/test.crd-6b204d94623b3df4370c": {
|
||||||
|
EntryPoints: []string{"web"},
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
|
||||||
|
Service: "default/test.crd-6b204d94623b3df4370c",
|
||||||
|
Priority: 14,
|
||||||
|
},
|
||||||
|
"default/test.crd-77c62dfe9517144aeeaa": {
|
||||||
|
EntryPoints: []string{"web"},
|
||||||
|
Service: "default/test.crd-77c62dfe9517144aeeaa",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/foo`)",
|
||||||
|
Priority: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{
|
||||||
|
"default/test.crd-6b204d94623b3df4370c": {
|
||||||
|
LoadBalancer: &config.LoadBalancerService{
|
||||||
|
Servers: []config.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Method: "wrr",
|
||||||
|
PassHostHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"default/test.crd-77c62dfe9517144aeeaa": {
|
||||||
|
LoadBalancer: &config.LoadBalancerService{
|
||||||
|
Servers: []config.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Method: "wrr",
|
||||||
|
PassHostHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "One ingress Route with two different services, their servers will merge",
|
||||||
|
paths: []string{"services.yml", "with_two_services.yml"},
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{
|
||||||
|
"default/test.crd-77c62dfe9517144aeeaa": {
|
||||||
|
EntryPoints: []string{"web"},
|
||||||
|
Service: "default/test.crd-77c62dfe9517144aeeaa",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/foo`)",
|
||||||
|
Priority: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{
|
||||||
|
"default/test.crd-77c62dfe9517144aeeaa": {
|
||||||
|
LoadBalancer: &config.LoadBalancerService{
|
||||||
|
Servers: []config.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.3:8080",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.4:8080",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Method: "wrr",
|
||||||
|
PassHostHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Ingress class",
|
||||||
|
paths: []string{"services.yml", "simple.yml"},
|
||||||
|
ingressClass: "tchouk",
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Route with empty rule value is ignored",
|
||||||
|
paths: []string{"services.yml", "with_no_rule_value.yml"},
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Route with kind not of a rule type (empty kind) is ignored",
|
||||||
|
paths: []string{"services.yml", "with_wrong_rule_kind.yml"},
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "check rule quoting validity",
|
||||||
|
paths: []string{"services.yml", "with_bad_host_rule.yml"},
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "TLS",
|
||||||
|
paths: []string{"services.yml", "with_tls.yml"},
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TLS: []*tls.Configuration{
|
||||||
|
{
|
||||||
|
Certificate: &tls.Certificate{
|
||||||
|
CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"),
|
||||||
|
KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{
|
||||||
|
"default/test.crd-6b204d94623b3df4370c": {
|
||||||
|
EntryPoints: []string{"web"},
|
||||||
|
Service: "default/test.crd-6b204d94623b3df4370c",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
|
||||||
|
Priority: 12,
|
||||||
|
TLS: &config.RouterTLSConfig{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{
|
||||||
|
"default/test.crd-6b204d94623b3df4370c": {
|
||||||
|
LoadBalancer: &config.LoadBalancerService{
|
||||||
|
Servers: []config.Server{
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.1:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "http://10.10.0.2:80",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Method: "wrr",
|
||||||
|
PassHostHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Simple Ingress Route, defaulting to https for servers",
|
||||||
|
paths: []string{"services.yml", "with_https_default.yml"},
|
||||||
|
expected: &config.Configuration{
|
||||||
|
TCP: &config.TCPConfiguration{},
|
||||||
|
HTTP: &config.HTTPConfiguration{
|
||||||
|
Routers: map[string]*config.Router{
|
||||||
|
"default/test.crd-6b204d94623b3df4370c": {
|
||||||
|
EntryPoints: []string{"foo"},
|
||||||
|
Service: "default/test.crd-6b204d94623b3df4370c",
|
||||||
|
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
|
||||||
|
Priority: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Middlewares: map[string]*config.Middleware{},
|
||||||
|
Services: map[string]*config.Service{
|
||||||
|
"default/test.crd-6b204d94623b3df4370c": {
|
||||||
|
LoadBalancer: &config.LoadBalancerService{
|
||||||
|
Servers: []config.Server{
|
||||||
|
{
|
||||||
|
URL: "https://10.10.0.5:443",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "https://10.10.0.6:443",
|
||||||
|
Weight: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Method: "wrr",
|
||||||
|
PassHostHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "port selected by name (TODO)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if test.expected == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p := Provider{IngressClass: test.ingressClass}
|
||||||
|
conf := p.loadConfigurationFromIngresses(context.Background(), newClientMock(test.paths...))
|
||||||
|
assert.Equal(t, test.expected, conf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
5
provider/kubernetes/crd/traefik/v1alpha1/doc.go
Normal file
5
provider/kubernetes/crd/traefik/v1alpha1/doc.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// +k8s:deepcopy-gen=package
|
||||||
|
|
||||||
|
// Package v1alpha1 is the v1alpha1 version of the API.
|
||||||
|
// +groupName=traefik.containo.us
|
||||||
|
package v1alpha1
|
72
provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go
Normal file
72
provider/kubernetes/crd/traefik/v1alpha1/ingressroute.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IngressRouteSpec is a specification for a IngressRouteSpec resource.
|
||||||
|
type IngressRouteSpec struct {
|
||||||
|
Routes []Route `json:"routes"`
|
||||||
|
EntryPoints []string `json:"entryPoints"`
|
||||||
|
TLS *TLS `json:"tls,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route contains the set of routes.
|
||||||
|
type Route struct {
|
||||||
|
Match string `json:"match"`
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
Services []Service `json:"services,omitempty"`
|
||||||
|
Middlewares []MiddlewareRef `json:"middlewares"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLS contains the TLS certificates configuration of the routes.
|
||||||
|
type TLS struct {
|
||||||
|
SecretName string `json:"secretName"`
|
||||||
|
// TODO MinimumProtocolVersion string `json:"minimumProtocolVersion,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service defines an upstream to proxy traffic.
|
||||||
|
type Service struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Port int32 `json:"port"`
|
||||||
|
// TODO Weight int `json:"weight,omitempty"`
|
||||||
|
HealthCheck *HealthCheck `json:"healthCheck,omitempty"`
|
||||||
|
Strategy string `json:"strategy,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MiddlewareRef is a ref to the Middleware resources.
|
||||||
|
type MiddlewareRef struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheck is the HealthCheck definition.
|
||||||
|
type HealthCheck struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Host string `json:"host,omitempty"`
|
||||||
|
Scheme string `json:"scheme"`
|
||||||
|
IntervalSeconds int64 `json:"intervalSeconds"`
|
||||||
|
TimeoutSeconds int64 `json:"timeoutSeconds"`
|
||||||
|
Headers map[string]string `json:"headers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +genclient
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// IngressRoute is an Ingress CRD specification.
|
||||||
|
type IngressRoute struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata"`
|
||||||
|
|
||||||
|
Spec IngressRouteSpec `json:"spec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// IngressRouteList is a list of IngressRoutes.
|
||||||
|
type IngressRouteList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata"`
|
||||||
|
Items []IngressRoute `json:"items"`
|
||||||
|
}
|
27
provider/kubernetes/crd/traefik/v1alpha1/middleware.go
Normal file
27
provider/kubernetes/crd/traefik/v1alpha1/middleware.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containous/traefik/config"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// +genclient
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// Middleware is a specification for a Middleware resource.
|
||||||
|
type Middleware struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata"`
|
||||||
|
|
||||||
|
Spec config.Middleware `json:"spec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
|
// MiddlewareList is a list of Middleware resources.
|
||||||
|
type MiddlewareList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata"`
|
||||||
|
|
||||||
|
Items []Middleware `json:"items"`
|
||||||
|
}
|
43
provider/kubernetes/crd/traefik/v1alpha1/register.go
Normal file
43
provider/kubernetes/crd/traefik/v1alpha1/register.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GroupName is the group name for Traefik.
|
||||||
|
const GroupName = "traefik.containo.us"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// SchemeBuilder collects the scheme builder functions.
|
||||||
|
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||||
|
|
||||||
|
// AddToScheme applies the SchemeBuilder functions to a specified scheme.
|
||||||
|
AddToScheme = SchemeBuilder.AddToScheme
|
||||||
|
)
|
||||||
|
|
||||||
|
// SchemeGroupVersion is group version used to register these objects.
|
||||||
|
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
|
||||||
|
|
||||||
|
// Kind takes an unqualified kind and returns back a Group qualified GroupKind.
|
||||||
|
func Kind(kind string) schema.GroupKind {
|
||||||
|
return SchemeGroupVersion.WithKind(kind).GroupKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resource takes an unqualified resource and returns a Group qualified GroupResource.
|
||||||
|
func Resource(resource string) schema.GroupResource {
|
||||||
|
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the list of known types to Scheme.
|
||||||
|
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||||
|
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||||
|
&IngressRoute{},
|
||||||
|
&IngressRouteList{},
|
||||||
|
&Middleware{},
|
||||||
|
&MiddlewareList{},
|
||||||
|
)
|
||||||
|
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2019 Containous SAS
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
import (
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *HealthCheck) DeepCopyInto(out *HealthCheck) {
|
||||||
|
*out = *in
|
||||||
|
if in.Headers != nil {
|
||||||
|
in, out := &in.Headers, &out.Headers
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HealthCheck.
|
||||||
|
func (in *HealthCheck) DeepCopy() *HealthCheck {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(HealthCheck)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *IngressRoute) DeepCopyInto(out *IngressRoute) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRoute.
|
||||||
|
func (in *IngressRoute) DeepCopy() *IngressRoute {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(IngressRoute)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *IngressRoute) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *IngressRouteList) DeepCopyInto(out *IngressRouteList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
out.ListMeta = in.ListMeta
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]IngressRoute, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteList.
|
||||||
|
func (in *IngressRouteList) DeepCopy() *IngressRouteList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(IngressRouteList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *IngressRouteList) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *IngressRouteSpec) DeepCopyInto(out *IngressRouteSpec) {
|
||||||
|
*out = *in
|
||||||
|
if in.Routes != nil {
|
||||||
|
in, out := &in.Routes, &out.Routes
|
||||||
|
*out = make([]Route, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.EntryPoints != nil {
|
||||||
|
in, out := &in.EntryPoints, &out.EntryPoints
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.TLS != nil {
|
||||||
|
in, out := &in.TLS, &out.TLS
|
||||||
|
*out = new(TLS)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteSpec.
|
||||||
|
func (in *IngressRouteSpec) DeepCopy() *IngressRouteSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(IngressRouteSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Middleware) DeepCopyInto(out *Middleware) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Middleware.
|
||||||
|
func (in *Middleware) DeepCopy() *Middleware {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Middleware)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *Middleware) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *MiddlewareList) DeepCopyInto(out *MiddlewareList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
out.ListMeta = in.ListMeta
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]Middleware, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MiddlewareList.
|
||||||
|
func (in *MiddlewareList) DeepCopy() *MiddlewareList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(MiddlewareList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *MiddlewareList) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *MiddlewareRef) DeepCopyInto(out *MiddlewareRef) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MiddlewareRef.
|
||||||
|
func (in *MiddlewareRef) DeepCopy() *MiddlewareRef {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(MiddlewareRef)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Route) DeepCopyInto(out *Route) {
|
||||||
|
*out = *in
|
||||||
|
if in.Services != nil {
|
||||||
|
in, out := &in.Services, &out.Services
|
||||||
|
*out = make([]Service, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Middlewares != nil {
|
||||||
|
in, out := &in.Middlewares, &out.Middlewares
|
||||||
|
*out = make([]MiddlewareRef, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route.
|
||||||
|
func (in *Route) DeepCopy() *Route {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Route)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Service) DeepCopyInto(out *Service) {
|
||||||
|
*out = *in
|
||||||
|
if in.HealthCheck != nil {
|
||||||
|
in, out := &in.HealthCheck, &out.HealthCheck
|
||||||
|
*out = new(HealthCheck)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Service.
|
||||||
|
func (in *Service) DeepCopy() *Service {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Service)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *TLS) DeepCopyInto(out *TLS) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLS.
|
||||||
|
func (in *TLS) DeepCopy() *TLS {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(TLS)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
63
provider/kubernetes/ingress/builder_ingress_test.go
Normal file
63
provider/kubernetes/ingress/builder_ingress_test.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package ingress
|
||||||
|
|
||||||
|
import (
|
||||||
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildIngress(opts ...func(*extensionsv1beta1.Ingress)) *extensionsv1beta1.Ingress {
|
||||||
|
i := &extensionsv1beta1.Ingress{}
|
||||||
|
i.Kind = "Ingress"
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(i)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func iNamespace(value string) func(*extensionsv1beta1.Ingress) {
|
||||||
|
return func(i *extensionsv1beta1.Ingress) {
|
||||||
|
i.Namespace = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iRules(opts ...func(*extensionsv1beta1.IngressSpec)) func(*extensionsv1beta1.Ingress) {
|
||||||
|
return func(i *extensionsv1beta1.Ingress) {
|
||||||
|
s := &extensionsv1beta1.IngressSpec{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(s)
|
||||||
|
}
|
||||||
|
i.Spec = *s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iRule(opts ...func(*extensionsv1beta1.IngressRule)) func(*extensionsv1beta1.IngressSpec) {
|
||||||
|
return func(spec *extensionsv1beta1.IngressSpec) {
|
||||||
|
r := &extensionsv1beta1.IngressRule{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(r)
|
||||||
|
}
|
||||||
|
spec.Rules = append(spec.Rules, *r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iHost(name string) func(*extensionsv1beta1.IngressRule) {
|
||||||
|
return func(rule *extensionsv1beta1.IngressRule) {
|
||||||
|
rule.Host = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iTLSes(opts ...func(*extensionsv1beta1.IngressTLS)) func(*extensionsv1beta1.Ingress) {
|
||||||
|
return func(i *extensionsv1beta1.Ingress) {
|
||||||
|
for _, opt := range opts {
|
||||||
|
iTLS := extensionsv1beta1.IngressTLS{}
|
||||||
|
opt(&iTLS)
|
||||||
|
i.Spec.TLS = append(i.Spec.TLS, iTLS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iTLS(secret string, hosts ...string) func(*extensionsv1beta1.IngressTLS) {
|
||||||
|
return func(i *extensionsv1beta1.IngressTLS) {
|
||||||
|
i.SecretName = secret
|
||||||
|
i.Hosts = hosts
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package kubernetes
|
package ingress
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containous/traefik/old/log"
|
"github.com/containous/traefik/old/log"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/k8s"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
kubeerror "k8s.io/apimachinery/pkg/api/errors"
|
kubeerror "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -41,7 +42,7 @@ func (reh *resourceEventHandler) OnDelete(obj interface{}) {
|
||||||
// WatchAll starts the watch of the Provider resources and updates the stores.
|
// WatchAll starts the watch of the Provider resources and updates the stores.
|
||||||
// The stores can then be accessed via the Get* functions.
|
// The stores can then be accessed via the Get* functions.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
WatchAll(namespaces Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error)
|
WatchAll(namespaces k8s.Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error)
|
||||||
GetIngresses() []*extensionsv1beta1.Ingress
|
GetIngresses() []*extensionsv1beta1.Ingress
|
||||||
GetService(namespace, name string) (*corev1.Service, bool, error)
|
GetService(namespace, name string) (*corev1.Service, bool, error)
|
||||||
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
|
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
|
||||||
|
@ -49,24 +50,17 @@ type Client interface {
|
||||||
UpdateIngressStatus(namespace, name, ip, hostname string) error
|
UpdateIngressStatus(namespace, name, ip, hostname string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientImpl struct {
|
type clientWrapper struct {
|
||||||
clientset *kubernetes.Clientset
|
clientset *kubernetes.Clientset
|
||||||
factories map[string]informers.SharedInformerFactory
|
factories map[string]informers.SharedInformerFactory
|
||||||
ingressLabelSelector labels.Selector
|
ingressLabelSelector labels.Selector
|
||||||
isNamespaceAll bool
|
isNamespaceAll bool
|
||||||
watchedNamespaces Namespaces
|
watchedNamespaces k8s.Namespaces
|
||||||
}
|
|
||||||
|
|
||||||
func newClientImpl(clientset *kubernetes.Clientset) *clientImpl {
|
|
||||||
return &clientImpl{
|
|
||||||
clientset: clientset,
|
|
||||||
factories: make(map[string]informers.SharedInformerFactory),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newInClusterClient returns a new Provider client that is expected to run
|
// newInClusterClient returns a new Provider client that is expected to run
|
||||||
// inside the cluster.
|
// inside the cluster.
|
||||||
func newInClusterClient(endpoint string) (*clientImpl, error) {
|
func newInClusterClient(endpoint string) (*clientWrapper, error) {
|
||||||
config, err := rest.InClusterConfig()
|
config, err := rest.InClusterConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create in-cluster configuration: %s", err)
|
return nil, fmt.Errorf("failed to create in-cluster configuration: %s", err)
|
||||||
|
@ -79,7 +73,7 @@ func newInClusterClient(endpoint string) (*clientImpl, error) {
|
||||||
return createClientFromConfig(config)
|
return createClientFromConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newExternalClusterClientFromFile(file string) (*clientImpl, error) {
|
func newExternalClusterClientFromFile(file string) (*clientWrapper, error) {
|
||||||
configFromFlags, err := clientcmd.BuildConfigFromFlags("", file)
|
configFromFlags, err := clientcmd.BuildConfigFromFlags("", file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -90,7 +84,7 @@ func newExternalClusterClientFromFile(file string) (*clientImpl, error) {
|
||||||
// newExternalClusterClient returns a new Provider client that may run outside
|
// newExternalClusterClient returns a new Provider client that may run outside
|
||||||
// of the cluster.
|
// of the cluster.
|
||||||
// The endpoint parameter must not be empty.
|
// The endpoint parameter must not be empty.
|
||||||
func newExternalClusterClient(endpoint, token, caFilePath string) (*clientImpl, error) {
|
func newExternalClusterClient(endpoint, token, caFilePath string) (*clientWrapper, error) {
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
return nil, errors.New("endpoint missing for external cluster client")
|
return nil, errors.New("endpoint missing for external cluster client")
|
||||||
}
|
}
|
||||||
|
@ -111,7 +105,7 @@ func newExternalClusterClient(endpoint, token, caFilePath string) (*clientImpl,
|
||||||
return createClientFromConfig(config)
|
return createClientFromConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createClientFromConfig(c *rest.Config) (*clientImpl, error) {
|
func createClientFromConfig(c *rest.Config) (*clientWrapper, error) {
|
||||||
clientset, err := kubernetes.NewForConfig(c)
|
clientset, err := kubernetes.NewForConfig(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -120,18 +114,25 @@ func createClientFromConfig(c *rest.Config) (*clientImpl, error) {
|
||||||
return newClientImpl(clientset), nil
|
return newClientImpl(clientset), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newClientImpl(clientset *kubernetes.Clientset) *clientWrapper {
|
||||||
|
return &clientWrapper{
|
||||||
|
clientset: clientset,
|
||||||
|
factories: make(map[string]informers.SharedInformerFactory),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WatchAll starts namespace-specific controllers for all relevant kinds.
|
// WatchAll starts namespace-specific controllers for all relevant kinds.
|
||||||
func (c *clientImpl) WatchAll(namespaces Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
func (c *clientWrapper) WatchAll(namespaces k8s.Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
||||||
eventCh := make(chan interface{}, 1)
|
eventCh := make(chan interface{}, 1)
|
||||||
|
eventHandler := c.newResourceEventHandler(eventCh)
|
||||||
|
|
||||||
if len(namespaces) == 0 {
|
if len(namespaces) == 0 {
|
||||||
namespaces = Namespaces{metav1.NamespaceAll}
|
namespaces = k8s.Namespaces{metav1.NamespaceAll}
|
||||||
c.isNamespaceAll = true
|
c.isNamespaceAll = true
|
||||||
}
|
}
|
||||||
|
|
||||||
c.watchedNamespaces = namespaces
|
c.watchedNamespaces = namespaces
|
||||||
|
|
||||||
eventHandler := c.newResourceEventHandler(eventCh)
|
|
||||||
for _, ns := range namespaces {
|
for _, ns := range namespaces {
|
||||||
factory := informers.NewFilteredSharedInformerFactory(c.clientset, resyncPeriod, ns, nil)
|
factory := informers.NewFilteredSharedInformerFactory(c.clientset, resyncPeriod, ns, nil)
|
||||||
factory.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
|
factory.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
|
||||||
|
@ -165,7 +166,7 @@ func (c *clientImpl) WatchAll(namespaces Namespaces, stopCh <-chan struct{}) (<-
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIngresses returns all Ingresses for observed namespaces in the cluster.
|
// GetIngresses returns all Ingresses for observed namespaces in the cluster.
|
||||||
func (c *clientImpl) GetIngresses() []*extensionsv1beta1.Ingress {
|
func (c *clientWrapper) GetIngresses() []*extensionsv1beta1.Ingress {
|
||||||
var result []*extensionsv1beta1.Ingress
|
var result []*extensionsv1beta1.Ingress
|
||||||
for ns, factory := range c.factories {
|
for ns, factory := range c.factories {
|
||||||
ings, err := factory.Extensions().V1beta1().Ingresses().Lister().List(c.ingressLabelSelector)
|
ings, err := factory.Extensions().V1beta1().Ingresses().Lister().List(c.ingressLabelSelector)
|
||||||
|
@ -178,7 +179,7 @@ func (c *clientImpl) GetIngresses() []*extensionsv1beta1.Ingress {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIngressStatus updates an Ingress with a provided status.
|
// UpdateIngressStatus updates an Ingress with a provided status.
|
||||||
func (c *clientImpl) UpdateIngressStatus(namespace, name, ip, hostname string) error {
|
func (c *clientWrapper) UpdateIngressStatus(namespace, name, ip, hostname string) error {
|
||||||
if !c.isWatchedNamespace(namespace) {
|
if !c.isWatchedNamespace(namespace) {
|
||||||
return fmt.Errorf("failed to get ingress %s/%s: namespace is not within watched namespaces", namespace, name)
|
return fmt.Errorf("failed to get ingress %s/%s: namespace is not within watched namespaces", namespace, name)
|
||||||
}
|
}
|
||||||
|
@ -207,7 +208,7 @@ func (c *clientImpl) UpdateIngressStatus(namespace, name, ip, hostname string) e
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetService returns the named service from the given namespace.
|
// GetService returns the named service from the given namespace.
|
||||||
func (c *clientImpl) GetService(namespace, name string) (*corev1.Service, bool, error) {
|
func (c *clientWrapper) GetService(namespace, name string) (*corev1.Service, bool, error) {
|
||||||
if !c.isWatchedNamespace(namespace) {
|
if !c.isWatchedNamespace(namespace) {
|
||||||
return nil, false, fmt.Errorf("failed to get service %s/%s: namespace is not within watched namespaces", namespace, name)
|
return nil, false, fmt.Errorf("failed to get service %s/%s: namespace is not within watched namespaces", namespace, name)
|
||||||
}
|
}
|
||||||
|
@ -218,7 +219,7 @@ func (c *clientImpl) GetService(namespace, name string) (*corev1.Service, bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEndpoints returns the named endpoints from the given namespace.
|
// GetEndpoints returns the named endpoints from the given namespace.
|
||||||
func (c *clientImpl) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) {
|
func (c *clientWrapper) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) {
|
||||||
if !c.isWatchedNamespace(namespace) {
|
if !c.isWatchedNamespace(namespace) {
|
||||||
return nil, false, fmt.Errorf("failed to get endpoints %s/%s: namespace is not within watched namespaces", namespace, name)
|
return nil, false, fmt.Errorf("failed to get endpoints %s/%s: namespace is not within watched namespaces", namespace, name)
|
||||||
}
|
}
|
||||||
|
@ -229,7 +230,7 @@ func (c *clientImpl) GetEndpoints(namespace, name string) (*corev1.Endpoints, bo
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSecret returns the named secret from the given namespace.
|
// GetSecret returns the named secret from the given namespace.
|
||||||
func (c *clientImpl) GetSecret(namespace, name string) (*corev1.Secret, bool, error) {
|
func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool, error) {
|
||||||
if !c.isWatchedNamespace(namespace) {
|
if !c.isWatchedNamespace(namespace) {
|
||||||
return nil, false, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name)
|
return nil, false, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name)
|
||||||
}
|
}
|
||||||
|
@ -245,14 +246,14 @@ func (c *clientImpl) GetSecret(namespace, name string) (*corev1.Secret, bool, er
|
||||||
// The distinction is necessary because we index all informers on the special
|
// The distinction is necessary because we index all informers on the special
|
||||||
// identifier iff all-namespaces are requested but receive specific namespace
|
// identifier iff all-namespaces are requested but receive specific namespace
|
||||||
// identifiers from the Kubernetes API, so we have to bridge this gap.
|
// identifiers from the Kubernetes API, so we have to bridge this gap.
|
||||||
func (c *clientImpl) lookupNamespace(ns string) string {
|
func (c *clientWrapper) lookupNamespace(ns string) string {
|
||||||
if c.isNamespaceAll {
|
if c.isNamespaceAll {
|
||||||
return metav1.NamespaceAll
|
return metav1.NamespaceAll
|
||||||
}
|
}
|
||||||
return ns
|
return ns
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientImpl) newResourceEventHandler(events chan<- interface{}) cache.ResourceEventHandler {
|
func (c *clientWrapper) newResourceEventHandler(events chan<- interface{}) cache.ResourceEventHandler {
|
||||||
return &cache.FilteringResourceEventHandler{
|
return &cache.FilteringResourceEventHandler{
|
||||||
FilterFunc: func(obj interface{}) bool {
|
FilterFunc: func(obj interface{}) bool {
|
||||||
// Ignore Ingresses that do not match our custom label selector.
|
// Ignore Ingresses that do not match our custom label selector.
|
||||||
|
@ -287,7 +288,7 @@ func translateNotFoundError(err error) (bool, error) {
|
||||||
|
|
||||||
// isWatchedNamespace checks to ensure that the namespace is being watched before we request
|
// isWatchedNamespace checks to ensure that the namespace is being watched before we request
|
||||||
// it to ensure we don't panic by requesting an out-of-watch object.
|
// it to ensure we don't panic by requesting an out-of-watch object.
|
||||||
func (c *clientImpl) isWatchedNamespace(ns string) bool {
|
func (c *clientWrapper) isWatchedNamespace(ns string) bool {
|
||||||
if c.isNamespaceAll {
|
if c.isNamespaceAll {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
|
@ -1,21 +1,58 @@
|
||||||
package kubernetes
|
package ingress
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/provider/kubernetes/k8s"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
|
v1beta12 "k8s.io/api/extensions/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ Client = (*clientMock)(nil)
|
||||||
|
|
||||||
type clientMock struct {
|
type clientMock struct {
|
||||||
ingresses []*extensionsv1beta1.Ingress
|
ingresses []*extensionsv1beta1.Ingress
|
||||||
services []*corev1.Service
|
services []*corev1.Service
|
||||||
secrets []*corev1.Secret
|
secrets []*corev1.Secret
|
||||||
endpoints []*corev1.Endpoints
|
endpoints []*corev1.Endpoints
|
||||||
watchChan chan interface{}
|
|
||||||
|
|
||||||
apiServiceError error
|
apiServiceError error
|
||||||
apiSecretError error
|
apiSecretError error
|
||||||
apiEndpointsError error
|
apiEndpointsError error
|
||||||
apiIngressStatusError error
|
apiIngressStatusError error
|
||||||
|
|
||||||
|
watchChan chan interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClientMock(paths ...string) clientMock {
|
||||||
|
var c clientMock
|
||||||
|
|
||||||
|
for _, path := range paths {
|
||||||
|
yamlContent, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
k8sObjects := k8s.MustParseYaml(yamlContent)
|
||||||
|
for _, obj := range k8sObjects {
|
||||||
|
switch o := obj.(type) {
|
||||||
|
case *corev1.Service:
|
||||||
|
c.services = append(c.services, o)
|
||||||
|
case *corev1.Secret:
|
||||||
|
c.secrets = append(c.secrets, o)
|
||||||
|
case *corev1.Endpoints:
|
||||||
|
c.endpoints = append(c.endpoints, o)
|
||||||
|
case *v1beta12.Ingress:
|
||||||
|
c.ingresses = append(c.ingresses, o)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unknown runtime object %+v %T", o, o))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c clientMock) GetIngresses() []*extensionsv1beta1.Ingress {
|
func (c clientMock) GetIngresses() []*extensionsv1beta1.Ingress {
|
||||||
|
@ -62,7 +99,7 @@ func (c clientMock) GetSecret(namespace, name string) (*corev1.Secret, bool, err
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c clientMock) WatchAll(namespaces Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
func (c clientMock) WatchAll(namespaces k8s.Namespaces, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
||||||
return c.watchChan, nil
|
return c.watchChan, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package kubernetes
|
package ingress
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.1
|
||||||
|
- ip: 10.10.0.2
|
||||||
|
ports:
|
||||||
|
- name: tchouk
|
||||||
|
port: 8089
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: toto
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.11.0.1
|
||||||
|
- ip: 10.11.0.2
|
||||||
|
ports:
|
||||||
|
- name: tchouk
|
||||||
|
port: 8089
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
kind: Ingress
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: ""
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: traefik.tchouk
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /bar
|
||||||
|
backend:
|
||||||
|
serviceName: service1
|
||||||
|
servicePort: tchouk
|
||||||
|
- path: /foo
|
||||||
|
backend:
|
||||||
|
serviceName: service1
|
||||||
|
servicePort: carotte
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Ingress
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: ""
|
||||||
|
namespace: toto
|
||||||
|
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: toto.traefik.tchouk
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /bar
|
||||||
|
backend:
|
||||||
|
serviceName: service1
|
||||||
|
servicePort: tchouk
|
|
@ -0,0 +1,24 @@
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: tchouk
|
||||||
|
port: 80
|
||||||
|
clusterIp: 10.0.0.1
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: toto
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: tchouk
|
||||||
|
port: 80
|
||||||
|
clusterIp: 10.0.0.1
|
|
@ -0,0 +1,32 @@
|
||||||
|
kind: Endpoints
|
||||||
|
apiversion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.30.0.1
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.41.0.1
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Endpoints
|
||||||
|
apiversion: v1
|
||||||
|
metadata:
|
||||||
|
name: service2
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.1
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.21.0.1
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
|
@ -0,0 +1,22 @@
|
||||||
|
kind: Ingress
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: ""
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
backend:
|
||||||
|
serviceName: service1
|
||||||
|
servicePort: 80
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Ingress
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: ""
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
backend:
|
||||||
|
serviceName: service2
|
||||||
|
servicePort: 80
|
|
@ -0,0 +1,22 @@
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
clusterIp: 10.0.0.1
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service2
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
clusterIp: 10.0.0.1
|
|
@ -0,0 +1,15 @@
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.1
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.21.0.1
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
|
@ -0,0 +1,22 @@
|
||||||
|
kind: Ingress
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: ""
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: traefik.tchouk
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /bar
|
||||||
|
backend:
|
||||||
|
serviceName: service1
|
||||||
|
servicePort: 80
|
||||||
|
- host: traefik.courgette
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /carotte
|
||||||
|
backend:
|
||||||
|
serviceName: service1
|
||||||
|
servicePort: 80
|
|
@ -0,0 +1,10 @@
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
clusterIp: 10.0.0.1
|
|
@ -0,0 +1,15 @@
|
||||||
|
kind: Endpoints
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
subsets:
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.10.0.1
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
- addresses:
|
||||||
|
- ip: 10.21.0.1
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
|
@ -0,0 +1,19 @@
|
||||||
|
kind: Ingress
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: ""
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: traefik.tchouk
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /bar
|
||||||
|
backend:
|
||||||
|
serviceName: service1
|
||||||
|
servicePort: 80
|
||||||
|
- path: /foo
|
||||||
|
backend:
|
||||||
|
serviceName: service1
|
||||||
|
servicePort: 80
|
|
@ -0,0 +1,10 @@
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: service1
|
||||||
|
namespace: testing
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
clusterIp: 10.0.0.1
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue