Switched Kubernetes provider to new client implementation: https://github.com/kubernetes/client-go
This commit is contained in:
parent
82234cbbb2
commit
15540764a0
10 changed files with 510 additions and 1131 deletions
|
@ -102,3 +102,5 @@ import:
|
|||
- package: github.com/ArthurHlt/go-eureka-client
|
||||
subpackages:
|
||||
- eureka
|
||||
- package: k8s.io/client-go
|
||||
version: ^v1.5.0
|
||||
|
|
|
@ -1,167 +1,206 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// APIEndpoint defines the base path for kubernetes API resources.
|
||||
APIEndpoint = "/api/v1"
|
||||
extentionsEndpoint = "/apis/extensions/v1beta1"
|
||||
defaultIngress = "/ingresses"
|
||||
namespaces = "/namespaces/"
|
||||
"k8s.io/client-go/1.5/kubernetes"
|
||||
"k8s.io/client-go/1.5/pkg/api"
|
||||
"k8s.io/client-go/1.5/pkg/api/v1"
|
||||
"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/client-go/1.5/pkg/fields"
|
||||
"k8s.io/client-go/1.5/pkg/labels"
|
||||
"k8s.io/client-go/1.5/pkg/runtime"
|
||||
"k8s.io/client-go/1.5/pkg/watch"
|
||||
"k8s.io/client-go/1.5/rest"
|
||||
"k8s.io/client-go/1.5/tools/cache"
|
||||
)
|
||||
|
||||
// Client is a client for the Kubernetes master.
|
||||
type Client interface {
|
||||
GetIngresses(labelSelector string, predicate func(Ingress) bool) ([]Ingress, error)
|
||||
GetService(name, namespace string) (Service, error)
|
||||
GetEndpoints(name, namespace string) (Endpoints, error)
|
||||
WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error)
|
||||
GetIngresses(namespaces Namespaces) []*v1beta1.Ingress
|
||||
GetService(namespace, name string) (*v1.Service, bool, error)
|
||||
GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error)
|
||||
WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, error)
|
||||
}
|
||||
|
||||
type clientImpl struct {
|
||||
endpointURL string
|
||||
tls *tls.Config
|
||||
token string
|
||||
caCert []byte
|
||||
ingController *cache.Controller
|
||||
svcController *cache.Controller
|
||||
epController *cache.Controller
|
||||
|
||||
ingStore cache.Store
|
||||
svcStore cache.Store
|
||||
epStore cache.Store
|
||||
|
||||
clientset *kubernetes.Clientset
|
||||
}
|
||||
|
||||
// NewClient returns a new Kubernetes client.
|
||||
// The provided host is an url (scheme://hostname[:port]) of a
|
||||
// Kubernetes master without any path.
|
||||
// The provided client is an authorized http.Client used to perform requests to the Kubernetes API master.
|
||||
func NewClient(baseURL string, caCert []byte, token string) (Client, error) {
|
||||
validURL, err := url.Parse(baseURL)
|
||||
// NewInClusterClient returns a new Kubernetes client.
|
||||
// WatchAll starts the watch of the Kubernetes ressources and updates the stores.
|
||||
// The stores can be accessed via the Get* functions.
|
||||
func NewInClusterClient() (Client, error) {
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse URL %q: %v", baseURL, err)
|
||||
return nil, err
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &clientImpl{
|
||||
endpointURL: strings.TrimSuffix(validURL.String(), "/"),
|
||||
token: token,
|
||||
caCert: caCert,
|
||||
clientset: clientset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeQueryString(baseParams map[string]string, labelSelector string) (string, error) {
|
||||
if labelSelector != "" {
|
||||
baseParams["labelSelector"] = labelSelector
|
||||
}
|
||||
queryData, err := json.Marshal(baseParams)
|
||||
// NewInClusterClientWithEndpoint is the same as NewInClusterClient but uses the provided endpoint URL
|
||||
func NewInClusterClientWithEndpoint(endpoint string) (Client, error) {
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
return string(queryData), nil
|
||||
|
||||
config.Host = endpoint
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &clientImpl{
|
||||
clientset: clientset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetIngresses returns all ingresses in the cluster
|
||||
func (c *clientImpl) GetIngresses(labelSelector string, predicate func(Ingress) bool) ([]Ingress, error) {
|
||||
getURL := c.endpointURL + extentionsEndpoint + defaultIngress
|
||||
queryParams := map[string]string{}
|
||||
queryData, err := makeQueryString(queryParams, labelSelector)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Had problems constructing query string %s : %v", queryParams, err)
|
||||
}
|
||||
body, err := c.do(c.request(getURL, queryData))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ingresses request: GET %q : %v", getURL, err)
|
||||
}
|
||||
func (c *clientImpl) GetIngresses(namespaces Namespaces) []*v1beta1.Ingress {
|
||||
ingList := c.ingStore.List()
|
||||
result := make([]*v1beta1.Ingress, 0, len(ingList))
|
||||
|
||||
var ingressList IngressList
|
||||
if err := json.Unmarshal(body, &ingressList); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode list of ingress resources: %v", err)
|
||||
}
|
||||
ingresses := ingressList.Items[:0]
|
||||
for _, ingress := range ingressList.Items {
|
||||
if predicate(ingress) {
|
||||
ingresses = append(ingresses, ingress)
|
||||
for _, obj := range ingList {
|
||||
ingress := obj.(*v1beta1.Ingress)
|
||||
if HasNamespace(ingress, namespaces) {
|
||||
result = append(result, ingress)
|
||||
}
|
||||
}
|
||||
return ingresses, nil
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// WatchIngresses returns all ingresses in the cluster
|
||||
func (c *clientImpl) WatchIngresses(labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
getURL := c.endpointURL + extentionsEndpoint + defaultIngress
|
||||
return c.watch(getURL, labelSelector, stopCh)
|
||||
// WatchIngresses starts the watch of Kubernetes Ingresses resources and updates the corresponding store
|
||||
func (c *clientImpl) WatchIngresses(labelSelector labels.Selector, stopCh <-chan struct{}) chan interface{} {
|
||||
watchCh := make(chan interface{}, 10)
|
||||
|
||||
source := NewListWatchFromClient(
|
||||
c.clientset.ExtensionsClient,
|
||||
"ingresses",
|
||||
api.NamespaceAll,
|
||||
fields.Everything(),
|
||||
labelSelector)
|
||||
|
||||
c.ingStore, c.ingController = cache.NewInformer(
|
||||
source,
|
||||
&v1beta1.Ingress{},
|
||||
0,
|
||||
newResourceEventHandlerFuncs(watchCh))
|
||||
go c.ingController.Run(stopCh)
|
||||
|
||||
return watchCh
|
||||
}
|
||||
|
||||
func newResourceEventHandlerFuncs(events chan interface{}) cache.ResourceEventHandlerFuncs {
|
||||
|
||||
return cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) { events <- obj },
|
||||
UpdateFunc: func(old, new interface{}) { events <- new },
|
||||
DeleteFunc: func(obj interface{}) { events <- obj },
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// GetService returns the named service from the named namespace
|
||||
func (c *clientImpl) GetService(name, namespace string) (Service, error) {
|
||||
getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/services/" + name
|
||||
|
||||
body, err := c.do(c.request(getURL, ""))
|
||||
if err != nil {
|
||||
return Service{}, fmt.Errorf("failed to create services request: GET %q : %v", getURL, err)
|
||||
func (c *clientImpl) GetService(namespace, name string) (*v1.Service, bool, error) {
|
||||
var service *v1.Service
|
||||
item, exists, err := c.svcStore.GetByKey(namespace + "/" + name)
|
||||
if item != nil {
|
||||
service = item.(*v1.Service)
|
||||
}
|
||||
|
||||
var service Service
|
||||
if err := json.Unmarshal(body, &service); err != nil {
|
||||
return Service{}, fmt.Errorf("failed to decode service resource: %v", err)
|
||||
}
|
||||
return service, nil
|
||||
return service, exists, err
|
||||
}
|
||||
|
||||
// WatchServices returns all services in the cluster
|
||||
func (c *clientImpl) WatchServices(stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
getURL := c.endpointURL + APIEndpoint + "/services"
|
||||
return c.watch(getURL, "", stopCh)
|
||||
// WatchServices starts the watch of Kubernetes Service resources and updates the corresponding store
|
||||
func (c *clientImpl) WatchServices(stopCh <-chan struct{}) chan interface{} {
|
||||
watchCh := make(chan interface{}, 10)
|
||||
|
||||
source := cache.NewListWatchFromClient(
|
||||
c.clientset.CoreClient,
|
||||
"services",
|
||||
api.NamespaceAll,
|
||||
fields.Everything())
|
||||
|
||||
c.svcStore, c.svcController = cache.NewInformer(
|
||||
source,
|
||||
&v1.Service{},
|
||||
0,
|
||||
newResourceEventHandlerFuncs(watchCh))
|
||||
go c.svcController.Run(stopCh)
|
||||
|
||||
return watchCh
|
||||
}
|
||||
|
||||
// GetEndpoints returns the named Endpoints
|
||||
// Endpoints have the same name as the coresponding service
|
||||
func (c *clientImpl) GetEndpoints(name, namespace string) (Endpoints, error) {
|
||||
getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/endpoints/" + name
|
||||
func (c *clientImpl) GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error) {
|
||||
var endpoint *v1.Endpoints
|
||||
item, exists, err := c.epStore.GetByKey(namespace + "/" + name)
|
||||
|
||||
body, err := c.do(c.request(getURL, ""))
|
||||
if err != nil {
|
||||
return Endpoints{}, fmt.Errorf("failed to create endpoints request: GET %q : %v", getURL, err)
|
||||
if item != nil {
|
||||
endpoint = item.(*v1.Endpoints)
|
||||
}
|
||||
|
||||
var endpoints Endpoints
|
||||
if err := json.Unmarshal(body, &endpoints); err != nil {
|
||||
return Endpoints{}, fmt.Errorf("failed to decode endpoints resources: %v", err)
|
||||
}
|
||||
return endpoints, nil
|
||||
return endpoint, exists, err
|
||||
}
|
||||
|
||||
// WatchEndpoints returns endpoints in the cluster
|
||||
func (c *clientImpl) WatchEndpoints(stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
getURL := c.endpointURL + APIEndpoint + "/endpoints"
|
||||
return c.watch(getURL, "", stopCh)
|
||||
}
|
||||
|
||||
// WatchAll returns events in the cluster
|
||||
func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
// WatchEndpoints starts the watch of Kubernetes Endpoints resources and updates the corresponding store
|
||||
func (c *clientImpl) WatchEndpoints(stopCh <-chan struct{}) chan interface{} {
|
||||
watchCh := make(chan interface{}, 10)
|
||||
errCh := make(chan error, 10)
|
||||
|
||||
stopIngresses := make(chan bool)
|
||||
chanIngresses, chanIngressesErr, err := c.WatchIngresses(labelSelector, stopIngresses)
|
||||
source := cache.NewListWatchFromClient(
|
||||
c.clientset.CoreClient,
|
||||
"endpoints",
|
||||
api.NamespaceAll,
|
||||
fields.Everything())
|
||||
|
||||
c.epStore, c.epController = cache.NewInformer(
|
||||
source,
|
||||
&v1.Endpoints{},
|
||||
0,
|
||||
newResourceEventHandlerFuncs(watchCh))
|
||||
go c.epController.Run(stopCh)
|
||||
|
||||
return watchCh
|
||||
}
|
||||
|
||||
// WatchAll returns events in the cluster and updates the stores via informer
|
||||
// Filters ingresses by labelSelector
|
||||
func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, error) {
|
||||
watchCh := make(chan interface{}, 10)
|
||||
|
||||
kubeLabelSelector, err := labels.Parse(labelSelector)
|
||||
if err != nil {
|
||||
return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err)
|
||||
}
|
||||
stopServices := make(chan bool)
|
||||
chanServices, chanServicesErr, err := c.WatchServices(stopServices)
|
||||
if err != nil {
|
||||
return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err)
|
||||
}
|
||||
stopEndpoints := make(chan bool)
|
||||
chanEndpoints, chanEndpointsErr, err := c.WatchEndpoints(stopEndpoints)
|
||||
if err != nil {
|
||||
return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stopIngresses := make(chan struct{})
|
||||
chanIngresses := c.WatchIngresses(kubeLabelSelector, stopIngresses)
|
||||
|
||||
stopServices := make(chan struct{})
|
||||
chanServices := c.WatchServices(stopServices)
|
||||
|
||||
stopEndpoints := make(chan struct{})
|
||||
chanEndpoints := c.WatchEndpoints(stopEndpoints)
|
||||
|
||||
go func() {
|
||||
defer close(watchCh)
|
||||
defer close(errCh)
|
||||
defer close(stopIngresses)
|
||||
defer close(stopServices)
|
||||
defer close(stopEndpoints)
|
||||
|
@ -169,128 +208,63 @@ func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan in
|
|||
for {
|
||||
select {
|
||||
case <-stopCh:
|
||||
stopIngresses <- true
|
||||
stopServices <- true
|
||||
stopEndpoints <- true
|
||||
return
|
||||
case err := <-chanIngressesErr:
|
||||
errCh <- err
|
||||
case err := <-chanServicesErr:
|
||||
errCh <- err
|
||||
case err := <-chanEndpointsErr:
|
||||
errCh <- err
|
||||
case event := <-chanIngresses:
|
||||
watchCh <- event
|
||||
c.fireEvent(event, watchCh)
|
||||
case event := <-chanServices:
|
||||
watchCh <- event
|
||||
c.fireEvent(event, watchCh)
|
||||
case event := <-chanEndpoints:
|
||||
watchCh <- event
|
||||
c.fireEvent(event, watchCh)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return watchCh, errCh, nil
|
||||
return watchCh, nil
|
||||
}
|
||||
|
||||
func (c *clientImpl) do(request *gorequest.SuperAgent) ([]byte, error) {
|
||||
res, body, errs := request.EndBytes()
|
||||
if errs != nil {
|
||||
return nil, fmt.Errorf("failed to create request: GET %q : %v", request.Url, errs)
|
||||
// fireEvent checks if all controllers have synced before firing
|
||||
// Used after startup or a reconnect
|
||||
func (c *clientImpl) fireEvent(event interface{}, watchCh chan interface{}) {
|
||||
if c.ingController.HasSynced() && c.svcController.HasSynced() && c.epController.HasSynced() {
|
||||
watchCh <- event
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("http error %d GET %q: %q", res.StatusCode, request.Url, string(body))
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func (c *clientImpl) request(reqURL string, queryContent interface{}) *gorequest.SuperAgent {
|
||||
// Make request to Kubernetes API
|
||||
parsedURL, parseErr := url.Parse(reqURL)
|
||||
if parseErr != nil {
|
||||
log.Errorf("Had issues parsing url %s. Trying anyway.", reqURL)
|
||||
// HasNamespace checks if the ingress is in one of the namespaces
|
||||
func HasNamespace(ingress *v1beta1.Ingress, namespaces Namespaces) bool {
|
||||
if len(namespaces) == 0 {
|
||||
return true
|
||||
}
|
||||
request := gorequest.New().Get(reqURL)
|
||||
request.Transport.DisableKeepAlives = true
|
||||
|
||||
if parsedURL.Scheme == "https" {
|
||||
pool := x509.NewCertPool()
|
||||
pool.AppendCertsFromPEM(c.caCert)
|
||||
c.tls = &tls.Config{RootCAs: pool}
|
||||
request.TLSClientConfig(c.tls)
|
||||
}
|
||||
if len(c.token) > 0 {
|
||||
request.Header["Authorization"] = "Bearer " + c.token
|
||||
}
|
||||
request.Query(queryContent)
|
||||
return request
|
||||
}
|
||||
|
||||
// GenericObject generic object
|
||||
type GenericObject struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ListMeta `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func (c *clientImpl) watch(url string, labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
watchCh := make(chan interface{}, 10)
|
||||
errCh := make(chan error, 10)
|
||||
|
||||
// get version
|
||||
body, err := c.do(c.request(url, ""))
|
||||
if err != nil {
|
||||
return watchCh, errCh, fmt.Errorf("failed to do version request: GET %q : %v", url, err)
|
||||
}
|
||||
|
||||
var generic GenericObject
|
||||
if err := json.Unmarshal(body, &generic); err != nil {
|
||||
return watchCh, errCh, fmt.Errorf("failed to decode version %v", err)
|
||||
}
|
||||
resourceVersion := generic.ResourceVersion
|
||||
queryParams := map[string]string{"watch": "", "resourceVersion": resourceVersion}
|
||||
queryData, err := makeQueryString(queryParams, labelSelector)
|
||||
if err != nil {
|
||||
return watchCh, errCh, fmt.Errorf("Unable to construct query args")
|
||||
}
|
||||
request := c.request(url, queryData)
|
||||
req, err := request.MakeRequest()
|
||||
if err != nil {
|
||||
return watchCh, errCh, fmt.Errorf("failed to make watch request: GET %q : %v", url, err)
|
||||
}
|
||||
request.Client.Transport = request.Transport
|
||||
|
||||
res, err := request.Client.Do(req)
|
||||
if err != nil {
|
||||
return watchCh, errCh, fmt.Errorf("failed to do watch request: GET %q: %v", url, err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
finishCh := make(chan bool)
|
||||
defer close(finishCh)
|
||||
defer close(watchCh)
|
||||
defer close(errCh)
|
||||
go func() {
|
||||
defer res.Body.Close()
|
||||
for {
|
||||
var eventList interface{}
|
||||
if err := json.NewDecoder(res.Body).Decode(&eventList); err != nil {
|
||||
if !strings.Contains(err.Error(), "net/http: request canceled") {
|
||||
errCh <- fmt.Errorf("failed to decode watch event: GET %q : %v", url, err)
|
||||
}
|
||||
finishCh <- true
|
||||
return
|
||||
}
|
||||
watchCh <- eventList
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-stopCh:
|
||||
go func() {
|
||||
request.Transport.CancelRequest(req)
|
||||
}()
|
||||
<-finishCh
|
||||
return
|
||||
for _, n := range namespaces {
|
||||
if ingress.ObjectMeta.Namespace == n {
|
||||
return true
|
||||
}
|
||||
}()
|
||||
return watchCh, errCh, nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace, field selector and label selector.
|
||||
// Extends cache.NewListWatchFromClient to support labelSelector
|
||||
func NewListWatchFromClient(c cache.Getter, resource string, namespace string, fieldSelector fields.Selector, labelSelector labels.Selector) *cache.ListWatch {
|
||||
listFunc := func(options api.ListOptions) (runtime.Object, error) {
|
||||
return c.Get().
|
||||
Namespace(namespace).
|
||||
Resource(resource).
|
||||
VersionedParams(&options, api.ParameterCodec).
|
||||
FieldsSelectorParam(fieldSelector).
|
||||
LabelsSelectorParam(labelSelector).
|
||||
Do().
|
||||
Get()
|
||||
}
|
||||
watchFunc := func(options api.ListOptions) (watch.Interface, error) {
|
||||
return c.Get().
|
||||
Prefix("watch").
|
||||
Namespace(namespace).
|
||||
Resource(resource).
|
||||
VersionedParams(&options, api.ParameterCodec).
|
||||
FieldsSelectorParam(fieldSelector).
|
||||
LabelsSelectorParam(labelSelector).
|
||||
Watch()
|
||||
}
|
||||
return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
|
||||
}
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
package k8s
|
||||
|
||||
// Endpoints is a collection of endpoints that implement the actual service. Example:
|
||||
// Name: "mysvc",
|
||||
// Subsets: [
|
||||
// {
|
||||
// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}],
|
||||
// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}]
|
||||
// },
|
||||
// {
|
||||
// Addresses: [{"ip": "10.10.3.3"}],
|
||||
// Ports: [{"name": "a", "port": 93}, {"name": "b", "port": 76}]
|
||||
// },
|
||||
// ]
|
||||
type Endpoints struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// The set of all endpoints is the union of all subsets.
|
||||
Subsets []EndpointSubset
|
||||
}
|
||||
|
||||
// EndpointSubset is a group of addresses with a common set of ports. The
|
||||
// expanded set of endpoints is the Cartesian product of Addresses x Ports.
|
||||
// For example, given:
|
||||
// {
|
||||
// Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}],
|
||||
// Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}]
|
||||
// }
|
||||
// The resulting set of endpoints can be viewed as:
|
||||
// a: [ 10.10.1.1:8675, 10.10.2.2:8675 ],
|
||||
// b: [ 10.10.1.1:309, 10.10.2.2:309 ]
|
||||
type EndpointSubset struct {
|
||||
Addresses []EndpointAddress
|
||||
NotReadyAddresses []EndpointAddress
|
||||
Ports []EndpointPort
|
||||
}
|
||||
|
||||
// EndpointAddress is a tuple that describes single IP address.
|
||||
type EndpointAddress struct {
|
||||
// The IP of this endpoint.
|
||||
// IPv6 is also accepted but not fully supported on all platforms. Also, certain
|
||||
// kubernetes components, like kube-proxy, are not IPv6 ready.
|
||||
// TODO: This should allow hostname or IP, see #4447.
|
||||
IP string
|
||||
// Optional: Hostname of this endpoint
|
||||
// Meant to be used by DNS servers etc.
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
// Optional: The kubernetes object related to the entry point.
|
||||
TargetRef *ObjectReference
|
||||
}
|
||||
|
||||
// EndpointPort is a tuple that describes a single port.
|
||||
type EndpointPort struct {
|
||||
// The name of this port (corresponds to ServicePort.Name). Optional
|
||||
// if only one port is defined. Must be a DNS_LABEL.
|
||||
Name string
|
||||
|
||||
// The port number.
|
||||
Port int32
|
||||
|
||||
// The IP protocol for this port.
|
||||
Protocol Protocol
|
||||
}
|
||||
|
||||
// ObjectReference contains enough information to let you inspect or modify the referred object.
|
||||
type ObjectReference struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
UID UID `json:"uid,omitempty"`
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
ResourceVersion string `json:"resourceVersion,omitempty"`
|
||||
|
||||
// Optional. If referring to a piece of an object instead of an entire object, this string
|
||||
// should contain information to identify the sub-object. For example, if the object
|
||||
// reference is to a container within a pod, this would take on a value like:
|
||||
// "spec.containers{name}" (where "name" refers to the name of the container that triggered
|
||||
// the event) or if no container name is specified "spec.containers[2]" (container with
|
||||
// index 2 in this pod). This syntax is chosen only to have some well-defined way of
|
||||
// referencing a part of an object.
|
||||
// TODO: this design is not final and this field is subject to change in the future.
|
||||
FieldPath string `json:"fieldPath,omitempty"`
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
package k8s
|
||||
|
||||
// Ingress is a collection of rules that allow inbound connections to reach the
|
||||
// endpoints defined by a backend. An Ingress can be configured to give services
|
||||
// externally-reachable urls, load balance traffic, terminate SSL, offer name
|
||||
// based virtual hosting etc.
|
||||
type Ingress struct {
|
||||
TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
|
||||
ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec is the desired state of the Ingress.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
|
||||
Spec IngressSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status is the current state of the Ingress.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
|
||||
Status IngressStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// IngressList is a collection of Ingress.
|
||||
type IngressList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
// Standard object's metadata.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
|
||||
ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Items is the list of Ingress.
|
||||
Items []Ingress `json:"items"`
|
||||
}
|
||||
|
||||
// IngressSpec describes the Ingress the user wishes to exist.
|
||||
type IngressSpec struct {
|
||||
// A default backend capable of servicing requests that don't match any
|
||||
// rule. At least one of 'backend' or 'rules' must be specified. This field
|
||||
// is optional to allow the loadbalancer controller or defaulting logic to
|
||||
// specify a global default.
|
||||
Backend *IngressBackend `json:"backend,omitempty"`
|
||||
|
||||
// TLS configuration. Currently the Ingress only supports a single TLS
|
||||
// port, 443. If multiple members of this list specify different hosts, they
|
||||
// will be multiplexed on the same port according to the hostname specified
|
||||
// through the SNI TLS extension, if the ingress controller fulfilling the
|
||||
// ingress supports SNI.
|
||||
TLS []IngressTLS `json:"tls,omitempty"`
|
||||
|
||||
// A list of host rules used to configure the Ingress. If unspecified, or
|
||||
// no rule matches, all traffic is sent to the default backend.
|
||||
Rules []IngressRule `json:"rules,omitempty"`
|
||||
// TODO: Add the ability to specify load-balancer IP through claims
|
||||
}
|
||||
|
||||
// IngressTLS describes the transport layer security associated with an Ingress.
|
||||
type IngressTLS struct {
|
||||
// Hosts are a list of hosts included in the TLS certificate. The values in
|
||||
// this list must match the name/s used in the tlsSecret. Defaults to the
|
||||
// wildcard host setting for the loadbalancer controller fulfilling this
|
||||
// Ingress, if left unspecified.
|
||||
Hosts []string `json:"hosts,omitempty"`
|
||||
// SecretName is the name of the secret used to terminate SSL traffic on 443.
|
||||
// Field is left optional to allow SSL routing based on SNI hostname alone.
|
||||
// If the SNI host in a listener conflicts with the "Host" header field used
|
||||
// by an IngressRule, the SNI host is used for termination and value of the
|
||||
// Host header is used for routing.
|
||||
SecretName string `json:"secretName,omitempty"`
|
||||
// TODO: Consider specifying different modes of termination, protocols etc.
|
||||
}
|
||||
|
||||
// IngressStatus describe the current state of the Ingress.
|
||||
type IngressStatus struct {
|
||||
// LoadBalancer contains the current status of the load-balancer.
|
||||
LoadBalancer LoadBalancerStatus `json:"loadBalancer,omitempty"`
|
||||
}
|
||||
|
||||
// IngressRule represents the rules mapping the paths under a specified host to
|
||||
// the related backend services. Incoming requests are first evaluated for a host
|
||||
// match, then routed to the backend associated with the matching IngressRuleValue.
|
||||
type IngressRule struct {
|
||||
// Host is the fully qualified domain name of a network host, as defined
|
||||
// by RFC 3986. Note the following deviations from the "host" part of the
|
||||
// URI as defined in the RFC:
|
||||
// 1. IPs are not allowed. Currently an IngressRuleValue can only apply to the
|
||||
// IP in the Spec of the parent Ingress.
|
||||
// 2. The `:` delimiter is not respected because ports are not allowed.
|
||||
// Currently the port of an Ingress is implicitly :80 for http and
|
||||
// :443 for https.
|
||||
// Both these may change in the future.
|
||||
// Incoming requests are matched against the host before the IngressRuleValue.
|
||||
// If the host is unspecified, the Ingress routes all traffic based on the
|
||||
// specified IngressRuleValue.
|
||||
Host string `json:"host,omitempty"`
|
||||
// IngressRuleValue represents a rule to route requests for this IngressRule.
|
||||
// If unspecified, the rule defaults to a http catch-all. Whether that sends
|
||||
// just traffic matching the host to the default backend or all traffic to the
|
||||
// default backend, is left to the controller fulfilling the Ingress. Http is
|
||||
// currently the only supported IngressRuleValue.
|
||||
IngressRuleValue `json:",inline,omitempty"`
|
||||
}
|
||||
|
||||
// IngressRuleValue represents a rule to apply against incoming requests. If the
|
||||
// rule is satisfied, the request is routed to the specified backend. Currently
|
||||
// mixing different types of rules in a single Ingress is disallowed, so exactly
|
||||
// one of the following must be set.
|
||||
type IngressRuleValue struct {
|
||||
//TODO:
|
||||
// 1. Consider renaming this resource and the associated rules so they
|
||||
// aren't tied to Ingress. They can be used to route intra-cluster traffic.
|
||||
// 2. Consider adding fields for ingress-type specific global options
|
||||
// usable by a loadbalancer, like http keep-alive.
|
||||
|
||||
HTTP *HTTPIngressRuleValue `json:"http,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPIngressRuleValue is a list of http selectors pointing to backends.
|
||||
// In the example: http://<host>/<path>?<searchpart> -> backend where
|
||||
// where parts of the url correspond to RFC 3986, this resource will be used
|
||||
// to match against everything after the last '/' and before the first '?'
|
||||
// or '#'.
|
||||
type HTTPIngressRuleValue struct {
|
||||
// A collection of paths that map requests to backends.
|
||||
Paths []HTTPIngressPath `json:"paths"`
|
||||
// TODO: Consider adding fields for ingress-type specific global
|
||||
// options usable by a loadbalancer, like http keep-alive.
|
||||
}
|
||||
|
||||
// HTTPIngressPath associates a path regex with a backend. Incoming urls matching
|
||||
// the path are forwarded to the backend.
|
||||
type HTTPIngressPath struct {
|
||||
// Path is a extended POSIX regex as defined by IEEE Std 1003.1,
|
||||
// (i.e this follows the egrep/unix syntax, not the perl syntax)
|
||||
// matched against the path of an incoming request. Currently it can
|
||||
// contain characters disallowed from the conventional "path"
|
||||
// part of a URL as defined by RFC 3986. Paths must begin with
|
||||
// a '/'. If unspecified, the path defaults to a catch all sending
|
||||
// traffic to the backend.
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
// Backend defines the referenced service endpoint to which the traffic
|
||||
// will be forwarded to.
|
||||
Backend IngressBackend `json:"backend"`
|
||||
}
|
||||
|
||||
// IngressBackend describes all endpoints for a given service and port.
|
||||
type IngressBackend struct {
|
||||
// Specifies the name of the referenced service.
|
||||
ServiceName string `json:"serviceName"`
|
||||
|
||||
// Specifies the port of the referenced service.
|
||||
ServicePort IntOrString `json:"servicePort"`
|
||||
}
|
32
provider/k8s/namespace.go
Normal file
32
provider/k8s/namespace.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Namespaces holds kubernetes namespaces
|
||||
type Namespaces []string
|
||||
|
||||
//Set adds strings elem into the the parser
|
||||
//it splits str on , and ;
|
||||
func (ns *Namespaces) Set(str string) error {
|
||||
fargs := func(c rune) bool {
|
||||
return c == ',' || c == ';'
|
||||
}
|
||||
// get function
|
||||
slice := strings.FieldsFunc(str, fargs)
|
||||
*ns = append(*ns, slice...)
|
||||
return nil
|
||||
}
|
||||
|
||||
//Get []string
|
||||
func (ns *Namespaces) Get() interface{} { return Namespaces(*ns) }
|
||||
|
||||
//String return slice in a string
|
||||
func (ns *Namespaces) String() string { return fmt.Sprintf("%v", *ns) }
|
||||
|
||||
//SetValue sets []string into the parser
|
||||
func (ns *Namespaces) SetValue(val interface{}) {
|
||||
*ns = Namespaces(val.(Namespaces))
|
||||
}
|
|
@ -1,326 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TypeMeta describes an individual object in an API response or request
|
||||
// with strings representing the type of the object and its API schema version.
|
||||
// Structures that are versioned or persisted should inline TypeMeta.
|
||||
type TypeMeta struct {
|
||||
// Kind is a string value representing the REST resource this object represents.
|
||||
// Servers may infer this from the endpoint the client submits requests to.
|
||||
// Cannot be updated.
|
||||
// In CamelCase.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
|
||||
Kind string `json:"kind,omitempty"`
|
||||
|
||||
// APIVersion defines the versioned schema of this representation of an object.
|
||||
// Servers should convert recognized schemas to the latest internal value, and
|
||||
// may reject unrecognized values.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
}
|
||||
|
||||
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
|
||||
// users must create.
|
||||
type ObjectMeta struct {
|
||||
// Name is unique within a namespace. Name is required when creating resources, although
|
||||
// some resources may allow a client to request the generation of an appropriate name
|
||||
// automatically. Name is primarily intended for creation idempotence and configuration
|
||||
// definition.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// GenerateName indicates that the name should be made unique by the server prior to persisting
|
||||
// it. A non-empty value for the field indicates the name will be made unique (and the name
|
||||
// returned to the client will be different than the name passed). The value of this field will
|
||||
// be combined with a unique suffix on the server if the Name field has not been provided.
|
||||
// The provided value must be valid within the rules for Name, and may be truncated by the length
|
||||
// of the suffix required to make the value unique on the server.
|
||||
//
|
||||
// If this field is specified, and Name is not present, the server will NOT return a 409 if the
|
||||
// generated name exists - instead, it will either return 201 Created or 500 with Reason
|
||||
// ServerTimeout indicating a unique name could not be found in the time allotted, and the client
|
||||
// should retry (optionally after the time indicated in the Retry-After header).
|
||||
GenerateName string `json:"generateName,omitempty"`
|
||||
|
||||
// Namespace defines the space within which name must be unique. An empty namespace is
|
||||
// equivalent to the "default" namespace, but "default" is the canonical representation.
|
||||
// Not all objects are required to be scoped to a namespace - the value of this field for
|
||||
// those objects will be empty.
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
|
||||
// SelfLink is a URL representing this object.
|
||||
SelfLink string `json:"selfLink,omitempty"`
|
||||
|
||||
// UID is the unique in time and space value for this object. It is typically generated by
|
||||
// the server on successful creation of a resource and is not allowed to change on PUT
|
||||
// operations.
|
||||
UID UID `json:"uid,omitempty"`
|
||||
|
||||
// An opaque value that represents the version of this resource. May be used for optimistic
|
||||
// concurrency, change detection, and the watch operation on a resource or set of resources.
|
||||
// Clients must treat these values as opaque and values may only be valid for a particular
|
||||
// resource or set of resources. Only servers will generate resource versions.
|
||||
ResourceVersion string `json:"resourceVersion,omitempty"`
|
||||
|
||||
// A sequence number representing a specific generation of the desired state.
|
||||
// Populated by the system. Read-only.
|
||||
Generation int64 `json:"generation,omitempty"`
|
||||
|
||||
// CreationTimestamp is a timestamp representing the server time when this object was
|
||||
// created. It is not guaranteed to be set in happens-before order across separate operations.
|
||||
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
|
||||
CreationTimestamp Time `json:"creationTimestamp,omitempty"`
|
||||
|
||||
// DeletionTimestamp is the time after which this resource will be deleted. This
|
||||
// field is set by the server when a graceful deletion is requested by the user, and is not
|
||||
// directly settable by a client. The resource will be deleted (no longer visible from
|
||||
// resource lists, and not reachable by name) after the time in this field. Once set, this
|
||||
// value may not be unset or be set further into the future, although it may be shortened
|
||||
// or the resource may be deleted prior to this time. For example, a user may request that
|
||||
// a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination
|
||||
// signal to the containers in the pod. Once the resource is deleted in the API, the Kubelet
|
||||
// will send a hard termination signal to the container.
|
||||
DeletionTimestamp *Time `json:"deletionTimestamp,omitempty"`
|
||||
|
||||
// DeletionGracePeriodSeconds records the graceful deletion value set when graceful deletion
|
||||
// was requested. Represents the most recent grace period, and may only be shortened once set.
|
||||
DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty"`
|
||||
|
||||
// Labels are key value pairs that may be used to scope and select individual resources.
|
||||
// Label keys are of the form:
|
||||
// label-key ::= prefixed-name | name
|
||||
// prefixed-name ::= prefix '/' name
|
||||
// prefix ::= DNS_SUBDOMAIN
|
||||
// name ::= DNS_LABEL
|
||||
// The prefix is optional. If the prefix is not specified, the key is assumed to be private
|
||||
// to the user. Other system components that wish to use labels must specify a prefix. The
|
||||
// "kubernetes.io/" prefix is reserved for use by kubernetes components.
|
||||
// TODO: replace map[string]string with labels.LabelSet type
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// Annotations are unstructured key value data stored with a resource that may be set by
|
||||
// external tooling. They are not queryable and should be preserved when modifying
|
||||
// objects. Annotation keys have the same formatting restrictions as Label keys. See the
|
||||
// comments on Labels for details.
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// UID is a type that holds unique ID values, including UUIDs. Because we
|
||||
// don't ONLY use UUIDs, this is an alias to string. Being a type captures
|
||||
// intent and helps make sure that UIDs and names do not get conflated.
|
||||
type UID string
|
||||
|
||||
// Time is a wrapper around time.Time which supports correct
|
||||
// marshaling to YAML and JSON. Wrappers are provided for many
|
||||
// of the factory methods that the time package offers.
|
||||
//
|
||||
// +protobuf.options.marshal=false
|
||||
// +protobuf.as=Timestamp
|
||||
type Time struct {
|
||||
time.Time `protobuf:"-"`
|
||||
}
|
||||
|
||||
// Service is a named abstraction of software service (for example, mysql) consisting of local port
|
||||
// (for example 3306) that the proxy listens on, and the selector that determines which pods
|
||||
// will answer requests sent through the proxy.
|
||||
type Service struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the behavior of a service.
|
||||
Spec ServiceSpec `json:"spec,omitempty"`
|
||||
|
||||
// Status represents the current status of a service.
|
||||
Status ServiceStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// ServiceSpec describes the attributes that a user creates on a service
|
||||
type ServiceSpec struct {
|
||||
// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer
|
||||
Type ServiceType `json:"type,omitempty"`
|
||||
|
||||
// Required: The list of ports that are exposed by this service.
|
||||
Ports []ServicePort `json:"ports"`
|
||||
|
||||
// This service will route traffic to pods having labels matching this selector. If empty or not present,
|
||||
// the service is assumed to have endpoints set by an external process and Kubernetes will not modify
|
||||
// those endpoints.
|
||||
Selector map[string]string `json:"selector"`
|
||||
|
||||
// ClusterIP is usually assigned by the master. If specified by the user
|
||||
// we will try to respect it or else fail the request. This field can
|
||||
// not be changed by updates.
|
||||
// Valid values are None, empty string (""), or a valid IP address
|
||||
// None can be specified for headless services when proxying is not required
|
||||
ClusterIP string `json:"clusterIP,omitempty"`
|
||||
|
||||
// ExternalIPs are used by external load balancers, or can be set by
|
||||
// users to handle external traffic that arrives at a node.
|
||||
ExternalIPs []string `json:"externalIPs,omitempty"`
|
||||
|
||||
// Only applies to Service Type: LoadBalancer
|
||||
// LoadBalancer will get created with the IP specified in this field.
|
||||
// This feature depends on whether the underlying cloud-provider supports specifying
|
||||
// the loadBalancerIP when a load balancer is created.
|
||||
// This field will be ignored if the cloud-provider does not support the feature.
|
||||
LoadBalancerIP string `json:"loadBalancerIP,omitempty"`
|
||||
|
||||
// Required: Supports "ClientIP" and "None". Used to maintain session affinity.
|
||||
SessionAffinity ServiceAffinity `json:"sessionAffinity,omitempty"`
|
||||
}
|
||||
|
||||
// ServicePort service port
|
||||
type ServicePort struct {
|
||||
// Optional if only one ServicePort is defined on this service: The
|
||||
// name of this port within the service. This must be a DNS_LABEL.
|
||||
// All ports within a ServiceSpec must have unique names. This maps to
|
||||
// the 'Name' field in EndpointPort objects.
|
||||
Name string `json:"name"`
|
||||
|
||||
// The IP protocol for this port. Supports "TCP" and "UDP".
|
||||
Protocol Protocol `json:"protocol"`
|
||||
|
||||
// The port that will be exposed on the service.
|
||||
Port int `json:"port"`
|
||||
|
||||
// Optional: The target port on pods selected by this service. If this
|
||||
// is a string, it will be looked up as a named port in the target
|
||||
// Pod's container ports. If this is not specified, the value
|
||||
// of the 'port' field is used (an identity map).
|
||||
// This field is ignored for services with clusterIP=None, and should be
|
||||
// omitted or set equal to the 'port' field.
|
||||
TargetPort IntOrString `json:"targetPort"`
|
||||
|
||||
// The port on each node on which this service is exposed.
|
||||
// Default is to auto-allocate a port if the ServiceType of this Service requires one.
|
||||
NodePort int `json:"nodePort"`
|
||||
}
|
||||
|
||||
// ServiceStatus represents the current status of a service
|
||||
type ServiceStatus struct {
|
||||
// LoadBalancer contains the current status of the load-balancer,
|
||||
// if one is present.
|
||||
LoadBalancer LoadBalancerStatus `json:"loadBalancer,omitempty"`
|
||||
}
|
||||
|
||||
// LoadBalancerStatus represents the status of a load-balancer
|
||||
type LoadBalancerStatus struct {
|
||||
// Ingress is a list containing ingress points for the load-balancer;
|
||||
// traffic intended for the service should be sent to these ingress points.
|
||||
Ingress []LoadBalancerIngress `json:"ingress,omitempty"`
|
||||
}
|
||||
|
||||
// LoadBalancerIngress represents the status of a load-balancer ingress point:
|
||||
// traffic intended for the service should be sent to an ingress point.
|
||||
type LoadBalancerIngress struct {
|
||||
// IP is set for load-balancer ingress points that are IP based
|
||||
// (typically GCE or OpenStack load-balancers)
|
||||
IP string `json:"ip,omitempty"`
|
||||
|
||||
// Hostname is set for load-balancer ingress points that are DNS based
|
||||
// (typically AWS load-balancers)
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
}
|
||||
|
||||
// ServiceAffinity Session Affinity Type string
|
||||
type ServiceAffinity string
|
||||
|
||||
// ServiceType Service Type string describes ingress methods for a service
|
||||
type ServiceType string
|
||||
|
||||
// Protocol defines network protocols supported for things like container ports.
|
||||
type Protocol string
|
||||
|
||||
// IntOrString is a type that can hold an int32 or a string. When used in
|
||||
// JSON or YAML marshalling and unmarshalling, it produces or consumes the
|
||||
// inner type. This allows you to have, for example, a JSON field that can
|
||||
// accept a name or number.
|
||||
// TODO: Rename to Int32OrString
|
||||
//
|
||||
// +protobuf=true
|
||||
// +protobuf.options.(gogoproto.goproto_stringer)=false
|
||||
type IntOrString struct {
|
||||
Type Type
|
||||
IntVal int32
|
||||
StrVal string
|
||||
}
|
||||
|
||||
// FromInt creates an IntOrString object with an int32 value. It is
|
||||
// your responsibility not to call this method with a value greater
|
||||
// than int32.
|
||||
// TODO: convert to (val int32)
|
||||
func FromInt(val int) IntOrString {
|
||||
return IntOrString{Type: Int, IntVal: int32(val)}
|
||||
}
|
||||
|
||||
// FromString creates an IntOrString object with a string value.
|
||||
func FromString(val string) IntOrString {
|
||||
return IntOrString{Type: String, StrVal: val}
|
||||
}
|
||||
|
||||
// String returns the string value, or the Itoa of the int value.
|
||||
func (intstr *IntOrString) String() string {
|
||||
if intstr.Type == String {
|
||||
return intstr.StrVal
|
||||
}
|
||||
return strconv.Itoa(intstr.IntValue())
|
||||
}
|
||||
|
||||
// IntValue returns the IntVal if type Int, or if
|
||||
// it is a String, will attempt a conversion to int.
|
||||
func (intstr *IntOrString) IntValue() int {
|
||||
if intstr.Type == String {
|
||||
i, _ := strconv.Atoi(intstr.StrVal)
|
||||
return i
|
||||
}
|
||||
return int(intstr.IntVal)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaller interface.
|
||||
func (intstr *IntOrString) UnmarshalJSON(value []byte) error {
|
||||
if value[0] == '"' {
|
||||
intstr.Type = String
|
||||
return json.Unmarshal(value, &intstr.StrVal)
|
||||
}
|
||||
intstr.Type = Int
|
||||
return json.Unmarshal(value, &intstr.IntVal)
|
||||
}
|
||||
|
||||
// Type represents the stored type of IntOrString.
|
||||
type Type int
|
||||
|
||||
const (
|
||||
// Int int
|
||||
Int Type = iota // The IntOrString holds an int.
|
||||
//String string
|
||||
String // The IntOrString holds a string.
|
||||
)
|
||||
|
||||
// ServiceList holds a list of services.
|
||||
type ServiceList struct {
|
||||
TypeMeta `json:",inline"`
|
||||
ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []Service `json:"items"`
|
||||
}
|
||||
|
||||
// ListMeta describes metadata that synthetic resources must have, including lists and
|
||||
// various status objects. A resource may have only one of {ObjectMeta, ListMeta}.
|
||||
type ListMeta struct {
|
||||
// SelfLink is a URL representing this object.
|
||||
// Populated by the system.
|
||||
// Read-only.
|
||||
SelfLink string `json:"selfLink,omitempty"`
|
||||
|
||||
// String that identifies the server's internal version of this object that
|
||||
// can be used by clients to determine when objects have changed.
|
||||
// Value must be treated as opaque by clients and passed unmodified back to the server.
|
||||
// Populated by the system.
|
||||
// Read-only.
|
||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency
|
||||
ResourceVersion string `json:"resourceVersion,omitempty"`
|
||||
}
|
|
@ -1,101 +1,49 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider/k8s"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/1.5/pkg/api/v1"
|
||||
"k8s.io/client-go/1.5/pkg/util/intstr"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider/k8s"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/traefik/job"
|
||||
)
|
||||
|
||||
const (
|
||||
serviceAccountToken = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||
serviceAccountCACert = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
|
||||
defaultKubeEndpoint = "http://127.0.0.1:8080"
|
||||
)
|
||||
|
||||
// Namespaces holds kubernetes namespaces
|
||||
type Namespaces []string
|
||||
|
||||
//Set adds strings elem into the the parser
|
||||
//it splits str on , and ;
|
||||
func (ns *Namespaces) Set(str string) error {
|
||||
fargs := func(c rune) bool {
|
||||
return c == ',' || c == ';'
|
||||
}
|
||||
// get function
|
||||
slice := strings.FieldsFunc(str, fargs)
|
||||
*ns = append(*ns, slice...)
|
||||
return nil
|
||||
}
|
||||
|
||||
//Get []string
|
||||
func (ns *Namespaces) Get() interface{} { return Namespaces(*ns) }
|
||||
|
||||
//String return slice in a string
|
||||
func (ns *Namespaces) String() string { return fmt.Sprintf("%v", *ns) }
|
||||
|
||||
//SetValue sets []string into the parser
|
||||
func (ns *Namespaces) SetValue(val interface{}) {
|
||||
*ns = Namespaces(val.(Namespaces))
|
||||
}
|
||||
|
||||
var _ Provider = (*Kubernetes)(nil)
|
||||
|
||||
// Kubernetes holds configurations of the Kubernetes provider.
|
||||
type Kubernetes struct {
|
||||
BaseProvider `mapstructure:",squash"`
|
||||
Endpoint string `description:"Kubernetes server endpoint"`
|
||||
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"`
|
||||
Namespaces Namespaces `description:"Kubernetes namespaces"`
|
||||
LabelSelector string `description:"Kubernetes api label selector to use"`
|
||||
Endpoint string `description:"Kubernetes server endpoint"`
|
||||
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"`
|
||||
Namespaces k8s.Namespaces `description:"Kubernetes namespaces"`
|
||||
LabelSelector string `description:"Kubernetes api label selector to use"`
|
||||
lastConfiguration safe.Safe
|
||||
}
|
||||
|
||||
func (provider *Kubernetes) createClient() (k8s.Client, error) {
|
||||
var token string
|
||||
tokenBytes, err := ioutil.ReadFile(serviceAccountToken)
|
||||
if err == nil {
|
||||
token = string(tokenBytes)
|
||||
log.Debugf("Kubernetes token: %s", token)
|
||||
} else {
|
||||
log.Errorf("Kubernetes load token error: %s", err)
|
||||
func (provider *Kubernetes) newK8sClient() (k8s.Client, error) {
|
||||
if provider.Endpoint != "" {
|
||||
log.Infof("Creating in cluster Kubernetes client with endpoint %", provider.Endpoint)
|
||||
return k8s.NewInClusterClientWithEndpoint(provider.Endpoint)
|
||||
}
|
||||
caCert, err := ioutil.ReadFile(serviceAccountCACert)
|
||||
if err == nil {
|
||||
log.Debugf("Kubernetes CA cert: %s", serviceAccountCACert)
|
||||
} else {
|
||||
log.Errorf("Kubernetes load token error: %s", err)
|
||||
}
|
||||
kubernetesHost := os.Getenv("KUBERNETES_SERVICE_HOST")
|
||||
kubernetesPort := os.Getenv("KUBERNETES_SERVICE_PORT_HTTPS")
|
||||
// Prioritize user provided kubernetes endpoint since kube container runtime will almost always have it
|
||||
if provider.Endpoint == "" && len(kubernetesPort) > 0 && len(kubernetesHost) > 0 {
|
||||
log.Debugf("Using environment provided kubernetes endpoint")
|
||||
provider.Endpoint = "https://" + kubernetesHost + ":" + kubernetesPort
|
||||
}
|
||||
if provider.Endpoint == "" {
|
||||
log.Debugf("Using default kubernetes api endpoint")
|
||||
provider.Endpoint = defaultKubeEndpoint
|
||||
}
|
||||
log.Debugf("Kubernetes endpoint: %s", provider.Endpoint)
|
||||
return k8s.NewClient(provider.Endpoint, caCert, token)
|
||||
log.Info("Creating in cluster Kubernetes client")
|
||||
return k8s.NewInClusterClient()
|
||||
}
|
||||
|
||||
// Provide allows the provider to provide configurations to traefik
|
||||
// using the given configuration channel.
|
||||
func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
|
||||
k8sClient, err := provider.createClient()
|
||||
k8sClient, err := provider.newK8sClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -107,7 +55,7 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
|
|||
stopWatch := make(chan bool, 5)
|
||||
defer close(stopWatch)
|
||||
log.Debugf("Using label selector: '%s'", provider.LabelSelector)
|
||||
eventsChan, errEventsChan, err := k8sClient.WatchAll(provider.LabelSelector, stopWatch)
|
||||
eventsChan, err := k8sClient.WatchAll(provider.LabelSelector, stopWatch)
|
||||
if err != nil {
|
||||
log.Errorf("Error watching kubernetes events: %v", err)
|
||||
timer := time.NewTimer(1 * time.Second)
|
||||
|
@ -123,9 +71,6 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
|
|||
case <-stop:
|
||||
stopWatch <- true
|
||||
return nil
|
||||
case err, _ := <-errEventsChan:
|
||||
stopWatch <- true
|
||||
return err
|
||||
case event := <-eventsChan:
|
||||
log.Debugf("Received event from kubernetes %+v", event)
|
||||
templateObjects, err := provider.loadIngresses(k8sClient)
|
||||
|
@ -133,7 +78,7 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
|
|||
return err
|
||||
}
|
||||
if reflect.DeepEqual(provider.lastConfiguration.Get(), templateObjects) {
|
||||
log.Debugf("Skipping event from kubernetes %+v", event)
|
||||
log.Debug("Skipping event")
|
||||
} else {
|
||||
provider.lastConfiguration.Set(templateObjects)
|
||||
configurationChan <- types.ConfigMessage{
|
||||
|
@ -155,39 +100,12 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
|
|||
}
|
||||
})
|
||||
|
||||
templateObjects, err := provider.loadIngresses(k8sClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if reflect.DeepEqual(provider.lastConfiguration.Get(), templateObjects) {
|
||||
log.Debugf("Skipping configuration from kubernetes %+v", templateObjects)
|
||||
} else {
|
||||
provider.lastConfiguration.Set(templateObjects)
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "kubernetes",
|
||||
Configuration: provider.loadConfig(*templateObjects),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configuration, error) {
|
||||
ingresses, err := k8sClient.GetIngresses(provider.LabelSelector, func(ingress k8s.Ingress) bool {
|
||||
if len(provider.Namespaces) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, n := range provider.Namespaces {
|
||||
if ingress.ObjectMeta.Namespace == n {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Error retrieving ingresses: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
ingresses := k8sClient.GetIngresses(provider.Namespaces)
|
||||
|
||||
templateObjects := types.Configuration{
|
||||
map[string]*types.Backend{},
|
||||
map[string]*types.Frontend{},
|
||||
|
@ -239,28 +157,28 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
|
|||
Rule: ruleType + ":" + pa.Path,
|
||||
}
|
||||
}
|
||||
service, err := k8sClient.GetService(pa.Backend.ServiceName, i.ObjectMeta.Namespace)
|
||||
if err != nil {
|
||||
log.Warnf("Error retrieving services: %v", err)
|
||||
service, exists, err := k8sClient.GetService(i.ObjectMeta.Namespace, pa.Backend.ServiceName)
|
||||
if err != nil || !exists {
|
||||
log.Warnf("Error retrieving service %s/%s: %v", i.ObjectMeta.Namespace, pa.Backend.ServiceName, err)
|
||||
delete(templateObjects.Frontends, r.Host+pa.Path)
|
||||
log.Warnf("Error retrieving services %s", pa.Backend.ServiceName)
|
||||
continue
|
||||
}
|
||||
|
||||
protocol := "http"
|
||||
for _, port := range service.Spec.Ports {
|
||||
if equalPorts(port, pa.Backend.ServicePort) {
|
||||
if port.Port == 443 {
|
||||
protocol = "https"
|
||||
}
|
||||
endpoints, err := k8sClient.GetEndpoints(service.ObjectMeta.Name, service.ObjectMeta.Namespace)
|
||||
if err != nil {
|
||||
log.Errorf("Error retrieving endpoints: %v", err)
|
||||
endpoints, exists, err := k8sClient.GetEndpoints(service.ObjectMeta.Namespace, service.ObjectMeta.Name)
|
||||
if err != nil || !exists {
|
||||
log.Errorf("Error retrieving endpoints %s/%s: %v", service.ObjectMeta.Namespace, service.ObjectMeta.Name, err)
|
||||
continue
|
||||
}
|
||||
if len(endpoints.Subsets) == 0 {
|
||||
log.Warnf("Endpoints not found for %s/%s, falling back to Service ClusterIP", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
|
||||
templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{
|
||||
URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(port.Port),
|
||||
URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(int(port.Port)),
|
||||
Weight: 1,
|
||||
}
|
||||
} else {
|
||||
|
@ -287,7 +205,7 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
|
|||
return &templateObjects, nil
|
||||
}
|
||||
|
||||
func endpointPortNumber(servicePort k8s.ServicePort, endpointPorts []k8s.EndpointPort) int {
|
||||
func endpointPortNumber(servicePort v1.ServicePort, endpointPorts []v1.EndpointPort) int {
|
||||
if len(endpointPorts) > 0 {
|
||||
//name is optional if there is only one port
|
||||
port := endpointPorts[0]
|
||||
|
@ -298,11 +216,11 @@ func endpointPortNumber(servicePort k8s.ServicePort, endpointPorts []k8s.Endpoin
|
|||
}
|
||||
return int(port.Port)
|
||||
}
|
||||
return servicePort.Port
|
||||
return int(servicePort.Port)
|
||||
}
|
||||
|
||||
func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool {
|
||||
if servicePort.Port == ingressPort.IntValue() {
|
||||
func equalPorts(servicePort v1.ServicePort, ingressPort intstr.IntOrString) bool {
|
||||
if int(servicePort.Port) == ingressPort.IntValue() {
|
||||
return true
|
||||
}
|
||||
if servicePort.Name != "" && servicePort.Name == ingressPort.String() {
|
||||
|
|
|
@ -2,29 +2,34 @@ package provider
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/containous/traefik/provider/k8s"
|
||||
"github.com/containous/traefik/types"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/1.5/pkg/api/v1"
|
||||
"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/client-go/1.5/pkg/util/intstr"
|
||||
|
||||
"github.com/containous/traefik/provider/k8s"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
func TestLoadIngresses(t *testing.T) {
|
||||
ingresses := []k8s.Ingress{{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ingresses := []*v1beta1.Ingress{{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "testing",
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(80),
|
||||
ServicePort: intstr.FromInt(80),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -33,19 +38,19 @@ func TestLoadIngresses(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Host: "bar",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service3",
|
||||
ServicePort: k8s.FromString("https"),
|
||||
ServicePort: intstr.FromString("https"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service2",
|
||||
ServicePort: k8s.FromInt(802),
|
||||
ServicePort: intstr.FromInt(802),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -55,16 +60,16 @@ func TestLoadIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}}
|
||||
services := []k8s.Service{
|
||||
services := []*v1.Service{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service1",
|
||||
UID: "1",
|
||||
Namespace: "testing",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Port: 80,
|
||||
},
|
||||
|
@ -72,14 +77,14 @@ func TestLoadIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service2",
|
||||
UID: "2",
|
||||
Namespace: "testing",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.2",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Port: 802,
|
||||
},
|
||||
|
@ -87,14 +92,14 @@ func TestLoadIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service3",
|
||||
UID: "3",
|
||||
Namespace: "testing",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.3",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
|
@ -107,33 +112,33 @@ func TestLoadIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
endpoints := []k8s.Endpoints{
|
||||
endpoints := []*v1.Endpoints{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service1",
|
||||
UID: "1",
|
||||
Namespace: "testing",
|
||||
},
|
||||
Subsets: []k8s.EndpointSubset{
|
||||
Subsets: []v1.EndpointSubset{
|
||||
{
|
||||
Addresses: []k8s.EndpointAddress{
|
||||
Addresses: []v1.EndpointAddress{
|
||||
{
|
||||
IP: "10.10.0.1",
|
||||
},
|
||||
},
|
||||
Ports: []k8s.EndpointPort{
|
||||
Ports: []v1.EndpointPort{
|
||||
{
|
||||
Port: 8080,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Addresses: []k8s.EndpointAddress{
|
||||
Addresses: []v1.EndpointAddress{
|
||||
{
|
||||
IP: "10.21.0.1",
|
||||
},
|
||||
},
|
||||
Ports: []k8s.EndpointPort{
|
||||
Ports: []v1.EndpointPort{
|
||||
{
|
||||
Port: 8080,
|
||||
},
|
||||
|
@ -142,19 +147,19 @@ func TestLoadIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service3",
|
||||
UID: "3",
|
||||
Namespace: "testing",
|
||||
},
|
||||
Subsets: []k8s.EndpointSubset{
|
||||
Subsets: []v1.EndpointSubset{
|
||||
{
|
||||
Addresses: []k8s.EndpointAddress{
|
||||
Addresses: []v1.EndpointAddress{
|
||||
{
|
||||
IP: "10.15.0.1",
|
||||
},
|
||||
},
|
||||
Ports: []k8s.EndpointPort{
|
||||
Ports: []v1.EndpointPort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 8080,
|
||||
|
@ -166,12 +171,12 @@ func TestLoadIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
Addresses: []k8s.EndpointAddress{
|
||||
Addresses: []v1.EndpointAddress{
|
||||
{
|
||||
IP: "10.15.0.2",
|
||||
},
|
||||
},
|
||||
Ports: []k8s.EndpointPort{
|
||||
Ports: []v1.EndpointPort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 9080,
|
||||
|
@ -267,23 +272,23 @@ func TestLoadIngresses(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRuleType(t *testing.T) {
|
||||
ingresses := []k8s.Ingress{
|
||||
ingresses := []*v1beta1.Ingress{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Annotations: map[string]string{"traefik.frontend.rule.type": "PathPrefixStrip"}, //camel case
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo1",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar1",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -294,21 +299,21 @@ func TestRuleType(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Annotations: map[string]string{"traefik.frontend.rule.type": "path"}, //lower case
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo1",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar2",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -319,21 +324,21 @@ func TestRuleType(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Annotations: map[string]string{"traefik.frontend.rule.type": "PathPrefix"}, //path prefix
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo2",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar1",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -344,21 +349,21 @@ func TestRuleType(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Annotations: map[string]string{"traefik.frontend.rule.type": "PathStrip"}, //path strip
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo2",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar2",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -369,21 +374,21 @@ func TestRuleType(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Annotations: map[string]string{"traefik.frontend.rule.type": "PathXXStrip"}, //wrong rule
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo1",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar3",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -394,15 +399,15 @@ func TestRuleType(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
services := []k8s.Service{
|
||||
services := []*v1.Service{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service1",
|
||||
UID: "1",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 801,
|
||||
|
@ -495,22 +500,22 @@ func TestRuleType(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetPassHostHeader(t *testing.T) {
|
||||
ingresses := []k8s.Ingress{{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ingresses := []*v1beta1.Ingress{{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "awesome",
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -520,16 +525,16 @@ func TestGetPassHostHeader(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}}
|
||||
services := []k8s.Service{
|
||||
services := []*v1.Service{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service1",
|
||||
Namespace: "awesome",
|
||||
UID: "1",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 801,
|
||||
|
@ -587,22 +592,22 @@ func TestGetPassHostHeader(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
|
||||
ingresses := []k8s.Ingress{
|
||||
ingresses := []*v1beta1.Ingress{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "awesome",
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service",
|
||||
ServicePort: k8s.FromInt(80),
|
||||
ServicePort: intstr.FromInt(80),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -613,16 +618,16 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
services := []k8s.Service{
|
||||
services := []*v1.Service{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service",
|
||||
UID: "1",
|
||||
Namespace: "awesome",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
|
@ -631,14 +636,14 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service",
|
||||
UID: "2",
|
||||
Namespace: "not-awesome",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.2",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
|
@ -693,23 +698,23 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoadNamespacedIngresses(t *testing.T) {
|
||||
ingresses := []k8s.Ingress{
|
||||
ingresses := []*v1beta1.Ingress{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "awesome",
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -718,19 +723,19 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Host: "bar",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service3",
|
||||
ServicePort: k8s.FromInt(443),
|
||||
ServicePort: intstr.FromInt(443),
|
||||
},
|
||||
},
|
||||
{
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service2",
|
||||
ServicePort: k8s.FromInt(802),
|
||||
ServicePort: intstr.FromInt(802),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -741,21 +746,21 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "not-awesome",
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "baz",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/baz",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -766,16 +771,16 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
services := []k8s.Service{
|
||||
services := []*v1.Service{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "awesome",
|
||||
Name: "service1",
|
||||
UID: "1",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 801,
|
||||
|
@ -784,14 +789,14 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service1",
|
||||
Namespace: "not-awesome",
|
||||
UID: "1",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 801,
|
||||
|
@ -800,14 +805,14 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service2",
|
||||
Namespace: "awesome",
|
||||
UID: "2",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.2",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Port: 802,
|
||||
},
|
||||
|
@ -815,14 +820,14 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service3",
|
||||
Namespace: "awesome",
|
||||
UID: "3",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.3",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 443,
|
||||
|
@ -906,23 +911,23 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
||||
ingresses := []k8s.Ingress{
|
||||
ingresses := []*v1beta1.Ingress{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "awesome",
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "foo",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -931,19 +936,19 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Host: "bar",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service3",
|
||||
ServicePort: k8s.FromInt(443),
|
||||
ServicePort: intstr.FromInt(443),
|
||||
},
|
||||
},
|
||||
{
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service2",
|
||||
ServicePort: k8s.FromInt(802),
|
||||
ServicePort: intstr.FromInt(802),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -954,21 +959,21 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "somewhat-awesome",
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "awesome",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/quix",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -979,21 +984,21 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "not-awesome",
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
Host: "baz",
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/baz",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1004,16 +1009,16 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
services := []k8s.Service{
|
||||
services := []*v1.Service{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service1",
|
||||
Namespace: "awesome",
|
||||
UID: "1",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 801,
|
||||
|
@ -1022,14 +1027,14 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "somewhat-awesome",
|
||||
Name: "service1",
|
||||
UID: "17",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.4",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 801,
|
||||
|
@ -1038,14 +1043,14 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "awesome",
|
||||
Name: "service2",
|
||||
UID: "2",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.2",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Port: 802,
|
||||
},
|
||||
|
@ -1053,14 +1058,14 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "awesome",
|
||||
Name: "service3",
|
||||
UID: "3",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.3",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 443,
|
||||
|
@ -1167,21 +1172,21 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestHostlessIngress(t *testing.T) {
|
||||
ingresses := []k8s.Ingress{{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ingresses := []*v1beta1.Ingress{{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: "awesome",
|
||||
},
|
||||
Spec: k8s.IngressSpec{
|
||||
Rules: []k8s.IngressRule{
|
||||
Spec: v1beta1.IngressSpec{
|
||||
Rules: []v1beta1.IngressRule{
|
||||
{
|
||||
IngressRuleValue: k8s.IngressRuleValue{
|
||||
HTTP: &k8s.HTTPIngressRuleValue{
|
||||
Paths: []k8s.HTTPIngressPath{
|
||||
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||
Paths: []v1beta1.HTTPIngressPath{
|
||||
{
|
||||
Path: "/bar",
|
||||
Backend: k8s.IngressBackend{
|
||||
Backend: v1beta1.IngressBackend{
|
||||
ServiceName: "service1",
|
||||
ServicePort: k8s.FromInt(801),
|
||||
ServicePort: intstr.FromInt(801),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1191,16 +1196,16 @@ func TestHostlessIngress(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}}
|
||||
services := []k8s.Service{
|
||||
services := []*v1.Service{
|
||||
{
|
||||
ObjectMeta: k8s.ObjectMeta{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "service1",
|
||||
Namespace: "awesome",
|
||||
UID: "1",
|
||||
},
|
||||
Spec: k8s.ServiceSpec{
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "10.0.0.1",
|
||||
Ports: []k8s.ServicePort{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 801,
|
||||
|
@ -1255,42 +1260,45 @@ func TestHostlessIngress(t *testing.T) {
|
|||
}
|
||||
|
||||
type clientMock struct {
|
||||
ingresses []k8s.Ingress
|
||||
services []k8s.Service
|
||||
endpoints []k8s.Endpoints
|
||||
ingresses []*v1beta1.Ingress
|
||||
services []*v1.Service
|
||||
endpoints []*v1.Endpoints
|
||||
watchChan chan interface{}
|
||||
}
|
||||
|
||||
func (c clientMock) GetIngresses(labelString string, predicate func(k8s.Ingress) bool) ([]k8s.Ingress, error) {
|
||||
var ingresses []k8s.Ingress
|
||||
func (c clientMock) GetIngresses(namespaces k8s.Namespaces) []*v1beta1.Ingress {
|
||||
result := make([]*v1beta1.Ingress, 0, len(c.ingresses))
|
||||
|
||||
for _, ingress := range c.ingresses {
|
||||
if predicate(ingress) {
|
||||
ingresses = append(ingresses, ingress)
|
||||
if k8s.HasNamespace(ingress, namespaces) {
|
||||
result = append(result, ingress)
|
||||
}
|
||||
}
|
||||
return ingresses, nil
|
||||
return result
|
||||
}
|
||||
func (c clientMock) WatchIngresses(labelString string, predicate func(k8s.Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
return c.watchChan, make(chan error), nil
|
||||
|
||||
func (c clientMock) WatchIngresses(labelSelector string, stopCh <-chan struct{}) chan interface{} {
|
||||
return c.watchChan
|
||||
}
|
||||
func (c clientMock) GetService(name, namespace string) (k8s.Service, error) {
|
||||
|
||||
func (c clientMock) GetService(namespace, name string) (*v1.Service, bool, error) {
|
||||
for _, service := range c.services {
|
||||
if service.Namespace == namespace && service.Name == name {
|
||||
return service, nil
|
||||
return service, true, nil
|
||||
}
|
||||
}
|
||||
return k8s.Service{}, nil
|
||||
return &v1.Service{}, true, nil
|
||||
}
|
||||
|
||||
func (c clientMock) GetEndpoints(name, namespace string) (k8s.Endpoints, error) {
|
||||
func (c clientMock) GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error) {
|
||||
for _, endpoints := range c.endpoints {
|
||||
if endpoints.Namespace == namespace && endpoints.Name == name {
|
||||
return endpoints, nil
|
||||
return endpoints, true, nil
|
||||
}
|
||||
}
|
||||
return k8s.Endpoints{}, nil
|
||||
return &v1.Endpoints{}, true, nil
|
||||
}
|
||||
|
||||
func (c clientMock) WatchAll(labelString string, stopCh <-chan bool) (chan interface{}, chan error, error) {
|
||||
return c.watchChan, make(chan error), nil
|
||||
func (c clientMock) WatchAll(labelString string, stopCh <-chan bool) (chan interface{}, error) {
|
||||
return c.watchChan, nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Fix for http://stackoverflow.com/questions/37284423/glog-flag-redefined-error
|
||||
# go test github.com/containous/traefik/provider
|
||||
#*/github.com/containous/traefik/provider/_test/provider.test flag redefined: log_dir
|
||||
#panic: */github.com/containous/traefik/provider/_test/provider.test flag redefined: log_dir
|
||||
rm -rf vendor/k8s.io/client-go/1.5/vendor/github.com/golang/glog/
|
||||
|
||||
go generate
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/k8s"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/containous/traefik/version"
|
||||
"github.com/docker/libkv/store"
|
||||
|
@ -144,7 +144,7 @@ Complete documentation is available at https://traefik.io`,
|
|||
f.AddParser(reflect.TypeOf(EntryPoints{}), &EntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
||||
f.AddParser(reflect.TypeOf(provider.Namespaces{}), &provider.Namespaces{})
|
||||
f.AddParser(reflect.TypeOf(k8s.Namespaces{}), &k8s.Namespaces{})
|
||||
f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{})
|
||||
|
||||
//add commands
|
||||
|
|
Loading…
Reference in a new issue