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
|
- package: github.com/ArthurHlt/go-eureka-client
|
||||||
subpackages:
|
subpackages:
|
||||||
- eureka
|
- eureka
|
||||||
|
- package: k8s.io/client-go
|
||||||
|
version: ^v1.5.0
|
||||||
|
|
|
@ -1,167 +1,206 @@
|
||||||
package k8s
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"k8s.io/client-go/1.5/kubernetes"
|
||||||
"crypto/x509"
|
"k8s.io/client-go/1.5/pkg/api"
|
||||||
"encoding/json"
|
"k8s.io/client-go/1.5/pkg/api/v1"
|
||||||
"fmt"
|
"k8s.io/client-go/1.5/pkg/apis/extensions/v1beta1"
|
||||||
"github.com/containous/traefik/log"
|
"k8s.io/client-go/1.5/pkg/fields"
|
||||||
"github.com/parnurzeal/gorequest"
|
"k8s.io/client-go/1.5/pkg/labels"
|
||||||
"net/http"
|
"k8s.io/client-go/1.5/pkg/runtime"
|
||||||
"net/url"
|
"k8s.io/client-go/1.5/pkg/watch"
|
||||||
"strings"
|
"k8s.io/client-go/1.5/rest"
|
||||||
)
|
"k8s.io/client-go/1.5/tools/cache"
|
||||||
|
|
||||||
const (
|
|
||||||
// APIEndpoint defines the base path for kubernetes API resources.
|
|
||||||
APIEndpoint = "/api/v1"
|
|
||||||
extentionsEndpoint = "/apis/extensions/v1beta1"
|
|
||||||
defaultIngress = "/ingresses"
|
|
||||||
namespaces = "/namespaces/"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is a client for the Kubernetes master.
|
// Client is a client for the Kubernetes master.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
GetIngresses(labelSelector string, predicate func(Ingress) bool) ([]Ingress, error)
|
GetIngresses(namespaces Namespaces) []*v1beta1.Ingress
|
||||||
GetService(name, namespace string) (Service, error)
|
GetService(namespace, name string) (*v1.Service, bool, error)
|
||||||
GetEndpoints(name, namespace string) (Endpoints, error)
|
GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error)
|
||||||
WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error)
|
WatchAll(labelSelector string, stopCh <-chan bool) (chan interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientImpl struct {
|
type clientImpl struct {
|
||||||
endpointURL string
|
ingController *cache.Controller
|
||||||
tls *tls.Config
|
svcController *cache.Controller
|
||||||
token string
|
epController *cache.Controller
|
||||||
caCert []byte
|
|
||||||
|
ingStore cache.Store
|
||||||
|
svcStore cache.Store
|
||||||
|
epStore cache.Store
|
||||||
|
|
||||||
|
clientset *kubernetes.Clientset
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient returns a new Kubernetes client.
|
// NewInClusterClient returns a new Kubernetes client.
|
||||||
// The provided host is an url (scheme://hostname[:port]) of a
|
// WatchAll starts the watch of the Kubernetes ressources and updates the stores.
|
||||||
// Kubernetes master without any path.
|
// The stores can be accessed via the Get* functions.
|
||||||
// The provided client is an authorized http.Client used to perform requests to the Kubernetes API master.
|
func NewInClusterClient() (Client, error) {
|
||||||
func NewClient(baseURL string, caCert []byte, token string) (Client, error) {
|
config, err := rest.InClusterConfig()
|
||||||
validURL, err := url.Parse(baseURL)
|
|
||||||
if err != nil {
|
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{
|
return &clientImpl{
|
||||||
endpointURL: strings.TrimSuffix(validURL.String(), "/"),
|
clientset: clientset,
|
||||||
token: token,
|
|
||||||
caCert: caCert,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeQueryString(baseParams map[string]string, labelSelector string) (string, error) {
|
// NewInClusterClientWithEndpoint is the same as NewInClusterClient but uses the provided endpoint URL
|
||||||
if labelSelector != "" {
|
func NewInClusterClientWithEndpoint(endpoint string) (Client, error) {
|
||||||
baseParams["labelSelector"] = labelSelector
|
config, err := rest.InClusterConfig()
|
||||||
}
|
|
||||||
queryData, err := json.Marshal(baseParams)
|
|
||||||
if err != nil {
|
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
|
// GetIngresses returns all ingresses in the cluster
|
||||||
func (c *clientImpl) GetIngresses(labelSelector string, predicate func(Ingress) bool) ([]Ingress, error) {
|
func (c *clientImpl) GetIngresses(namespaces Namespaces) []*v1beta1.Ingress {
|
||||||
getURL := c.endpointURL + extentionsEndpoint + defaultIngress
|
ingList := c.ingStore.List()
|
||||||
queryParams := map[string]string{}
|
result := make([]*v1beta1.Ingress, 0, len(ingList))
|
||||||
queryData, err := makeQueryString(queryParams, labelSelector)
|
|
||||||
if err != nil {
|
for _, obj := range ingList {
|
||||||
return nil, fmt.Errorf("Had problems constructing query string %s : %v", queryParams, err)
|
ingress := obj.(*v1beta1.Ingress)
|
||||||
|
if HasNamespace(ingress, namespaces) {
|
||||||
|
result = append(result, ingress)
|
||||||
}
|
}
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ingressList IngressList
|
return result
|
||||||
if err := json.Unmarshal(body, &ingressList); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to decode list of ingress resources: %v", err)
|
|
||||||
}
|
|
||||||
ingresses := ingressList.Items[:0]
|
|
||||||
for _, ingress := range ingressList.Items {
|
|
||||||
if predicate(ingress) {
|
|
||||||
ingresses = append(ingresses, ingress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ingresses, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchIngresses returns all ingresses in the cluster
|
// WatchIngresses starts the watch of Kubernetes Ingresses resources and updates the corresponding store
|
||||||
func (c *clientImpl) WatchIngresses(labelSelector string, stopCh <-chan bool) (chan interface{}, chan error, error) {
|
func (c *clientImpl) WatchIngresses(labelSelector labels.Selector, stopCh <-chan struct{}) chan interface{} {
|
||||||
getURL := c.endpointURL + extentionsEndpoint + defaultIngress
|
watchCh := make(chan interface{}, 10)
|
||||||
return c.watch(getURL, labelSelector, stopCh)
|
|
||||||
|
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
|
// GetService returns the named service from the named namespace
|
||||||
func (c *clientImpl) GetService(name, namespace string) (Service, error) {
|
func (c *clientImpl) GetService(namespace, name string) (*v1.Service, bool, error) {
|
||||||
getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/services/" + name
|
var service *v1.Service
|
||||||
|
item, exists, err := c.svcStore.GetByKey(namespace + "/" + name)
|
||||||
body, err := c.do(c.request(getURL, ""))
|
if item != nil {
|
||||||
if err != nil {
|
service = item.(*v1.Service)
|
||||||
return Service{}, fmt.Errorf("failed to create services request: GET %q : %v", getURL, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var service Service
|
return service, exists, err
|
||||||
if err := json.Unmarshal(body, &service); err != nil {
|
|
||||||
return Service{}, fmt.Errorf("failed to decode service resource: %v", err)
|
|
||||||
}
|
|
||||||
return service, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchServices returns all services in the cluster
|
// WatchServices starts the watch of Kubernetes Service resources and updates the corresponding store
|
||||||
func (c *clientImpl) WatchServices(stopCh <-chan bool) (chan interface{}, chan error, error) {
|
func (c *clientImpl) WatchServices(stopCh <-chan struct{}) chan interface{} {
|
||||||
getURL := c.endpointURL + APIEndpoint + "/services"
|
watchCh := make(chan interface{}, 10)
|
||||||
return c.watch(getURL, "", stopCh)
|
|
||||||
|
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
|
// GetEndpoints returns the named Endpoints
|
||||||
// Endpoints have the same name as the coresponding service
|
// Endpoints have the same name as the coresponding service
|
||||||
func (c *clientImpl) GetEndpoints(name, namespace string) (Endpoints, error) {
|
func (c *clientImpl) GetEndpoints(namespace, name string) (*v1.Endpoints, bool, error) {
|
||||||
getURL := c.endpointURL + APIEndpoint + namespaces + namespace + "/endpoints/" + name
|
var endpoint *v1.Endpoints
|
||||||
|
item, exists, err := c.epStore.GetByKey(namespace + "/" + name)
|
||||||
|
|
||||||
body, err := c.do(c.request(getURL, ""))
|
if item != nil {
|
||||||
if err != nil {
|
endpoint = item.(*v1.Endpoints)
|
||||||
return Endpoints{}, fmt.Errorf("failed to create endpoints request: GET %q : %v", getURL, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var endpoints Endpoints
|
return endpoint, exists, err
|
||||||
if err := json.Unmarshal(body, &endpoints); err != nil {
|
|
||||||
return Endpoints{}, fmt.Errorf("failed to decode endpoints resources: %v", err)
|
|
||||||
}
|
|
||||||
return endpoints, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchEndpoints returns endpoints in the cluster
|
// WatchEndpoints starts the watch of Kubernetes Endpoints resources and updates the corresponding store
|
||||||
func (c *clientImpl) WatchEndpoints(stopCh <-chan bool) (chan interface{}, chan error, error) {
|
func (c *clientImpl) WatchEndpoints(stopCh <-chan struct{}) chan interface{} {
|
||||||
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) {
|
|
||||||
watchCh := make(chan interface{}, 10)
|
watchCh := make(chan interface{}, 10)
|
||||||
errCh := make(chan error, 10)
|
|
||||||
|
|
||||||
stopIngresses := make(chan bool)
|
source := cache.NewListWatchFromClient(
|
||||||
chanIngresses, chanIngressesErr, err := c.WatchIngresses(labelSelector, stopIngresses)
|
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 {
|
if err != nil {
|
||||||
return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err)
|
return nil, 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
go func() {
|
||||||
defer close(watchCh)
|
defer close(watchCh)
|
||||||
defer close(errCh)
|
|
||||||
defer close(stopIngresses)
|
defer close(stopIngresses)
|
||||||
defer close(stopServices)
|
defer close(stopServices)
|
||||||
defer close(stopEndpoints)
|
defer close(stopEndpoints)
|
||||||
|
@ -169,128 +208,63 @@ func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan in
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-stopCh:
|
case <-stopCh:
|
||||||
stopIngresses <- true
|
|
||||||
stopServices <- true
|
|
||||||
stopEndpoints <- true
|
|
||||||
return
|
return
|
||||||
case err := <-chanIngressesErr:
|
|
||||||
errCh <- err
|
|
||||||
case err := <-chanServicesErr:
|
|
||||||
errCh <- err
|
|
||||||
case err := <-chanEndpointsErr:
|
|
||||||
errCh <- err
|
|
||||||
case event := <-chanIngresses:
|
case event := <-chanIngresses:
|
||||||
watchCh <- event
|
c.fireEvent(event, watchCh)
|
||||||
case event := <-chanServices:
|
case event := <-chanServices:
|
||||||
watchCh <- event
|
c.fireEvent(event, watchCh)
|
||||||
case event := <-chanEndpoints:
|
case event := <-chanEndpoints:
|
||||||
|
c.fireEvent(event, watchCh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return watchCh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
watchCh <- event
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return watchCh, errCh, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientImpl) do(request *gorequest.SuperAgent) ([]byte, error) {
|
// HasNamespace checks if the ingress is in one of the namespaces
|
||||||
res, body, errs := request.EndBytes()
|
func HasNamespace(ingress *v1beta1.Ingress, namespaces Namespaces) bool {
|
||||||
if errs != nil {
|
if len(namespaces) == 0 {
|
||||||
return nil, fmt.Errorf("failed to create request: GET %q : %v", request.Url, errs)
|
return true
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
for _, n := range namespaces {
|
||||||
if res.StatusCode != http.StatusOK {
|
if ingress.ObjectMeta.Namespace == n {
|
||||||
return nil, fmt.Errorf("http error %d GET %q: %q", res.StatusCode, request.Url, string(body))
|
return true
|
||||||
}
|
}
|
||||||
return body, nil
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientImpl) request(reqURL string, queryContent interface{}) *gorequest.SuperAgent {
|
// NewListWatchFromClient creates a new ListWatch from the specified client, resource, namespace, field selector and label selector.
|
||||||
// Make request to Kubernetes API
|
// Extends cache.NewListWatchFromClient to support labelSelector
|
||||||
parsedURL, parseErr := url.Parse(reqURL)
|
func NewListWatchFromClient(c cache.Getter, resource string, namespace string, fieldSelector fields.Selector, labelSelector labels.Selector) *cache.ListWatch {
|
||||||
if parseErr != nil {
|
listFunc := func(options api.ListOptions) (runtime.Object, error) {
|
||||||
log.Errorf("Had issues parsing url %s. Trying anyway.", reqURL)
|
return c.Get().
|
||||||
|
Namespace(namespace).
|
||||||
|
Resource(resource).
|
||||||
|
VersionedParams(&options, api.ParameterCodec).
|
||||||
|
FieldsSelectorParam(fieldSelector).
|
||||||
|
LabelsSelectorParam(labelSelector).
|
||||||
|
Do().
|
||||||
|
Get()
|
||||||
}
|
}
|
||||||
request := gorequest.New().Get(reqURL)
|
watchFunc := func(options api.ListOptions) (watch.Interface, error) {
|
||||||
request.Transport.DisableKeepAlives = true
|
return c.Get().
|
||||||
|
Prefix("watch").
|
||||||
if parsedURL.Scheme == "https" {
|
Namespace(namespace).
|
||||||
pool := x509.NewCertPool()
|
Resource(resource).
|
||||||
pool.AppendCertsFromPEM(c.caCert)
|
VersionedParams(&options, api.ParameterCodec).
|
||||||
c.tls = &tls.Config{RootCAs: pool}
|
FieldsSelectorParam(fieldSelector).
|
||||||
request.TLSClientConfig(c.tls)
|
LabelsSelectorParam(labelSelector).
|
||||||
|
Watch()
|
||||||
}
|
}
|
||||||
if len(c.token) > 0 {
|
return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
|
||||||
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
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return watchCh, errCh, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,55 +1,24 @@
|
||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
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"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"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/cenk/backoff"
|
||||||
"github.com/containous/traefik/job"
|
"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)
|
var _ Provider = (*Kubernetes)(nil)
|
||||||
|
|
||||||
// Kubernetes holds configurations of the Kubernetes provider.
|
// Kubernetes holds configurations of the Kubernetes provider.
|
||||||
|
@ -57,45 +26,24 @@ type Kubernetes struct {
|
||||||
BaseProvider `mapstructure:",squash"`
|
BaseProvider `mapstructure:",squash"`
|
||||||
Endpoint string `description:"Kubernetes server endpoint"`
|
Endpoint string `description:"Kubernetes server endpoint"`
|
||||||
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"`
|
DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers"`
|
||||||
Namespaces Namespaces `description:"Kubernetes namespaces"`
|
Namespaces k8s.Namespaces `description:"Kubernetes namespaces"`
|
||||||
LabelSelector string `description:"Kubernetes api label selector to use"`
|
LabelSelector string `description:"Kubernetes api label selector to use"`
|
||||||
lastConfiguration safe.Safe
|
lastConfiguration safe.Safe
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *Kubernetes) createClient() (k8s.Client, error) {
|
func (provider *Kubernetes) newK8sClient() (k8s.Client, error) {
|
||||||
var token string
|
if provider.Endpoint != "" {
|
||||||
tokenBytes, err := ioutil.ReadFile(serviceAccountToken)
|
log.Infof("Creating in cluster Kubernetes client with endpoint %", provider.Endpoint)
|
||||||
if err == nil {
|
return k8s.NewInClusterClientWithEndpoint(provider.Endpoint)
|
||||||
token = string(tokenBytes)
|
|
||||||
log.Debugf("Kubernetes token: %s", token)
|
|
||||||
} else {
|
|
||||||
log.Errorf("Kubernetes load token error: %s", err)
|
|
||||||
}
|
}
|
||||||
caCert, err := ioutil.ReadFile(serviceAccountCACert)
|
log.Info("Creating in cluster Kubernetes client")
|
||||||
if err == nil {
|
return k8s.NewInClusterClient()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provide allows the provider to provide configurations to traefik
|
// Provide allows the provider to provide configurations to traefik
|
||||||
// using the given configuration channel.
|
// using the given configuration channel.
|
||||||
func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints []types.Constraint) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -107,7 +55,7 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
|
||||||
stopWatch := make(chan bool, 5)
|
stopWatch := make(chan bool, 5)
|
||||||
defer close(stopWatch)
|
defer close(stopWatch)
|
||||||
log.Debugf("Using label selector: '%s'", provider.LabelSelector)
|
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 {
|
if err != nil {
|
||||||
log.Errorf("Error watching kubernetes events: %v", err)
|
log.Errorf("Error watching kubernetes events: %v", err)
|
||||||
timer := time.NewTimer(1 * time.Second)
|
timer := time.NewTimer(1 * time.Second)
|
||||||
|
@ -123,9 +71,6 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
|
||||||
case <-stop:
|
case <-stop:
|
||||||
stopWatch <- true
|
stopWatch <- true
|
||||||
return nil
|
return nil
|
||||||
case err, _ := <-errEventsChan:
|
|
||||||
stopWatch <- true
|
|
||||||
return err
|
|
||||||
case event := <-eventsChan:
|
case event := <-eventsChan:
|
||||||
log.Debugf("Received event from kubernetes %+v", event)
|
log.Debugf("Received event from kubernetes %+v", event)
|
||||||
templateObjects, err := provider.loadIngresses(k8sClient)
|
templateObjects, err := provider.loadIngresses(k8sClient)
|
||||||
|
@ -133,7 +78,7 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if reflect.DeepEqual(provider.lastConfiguration.Get(), templateObjects) {
|
if reflect.DeepEqual(provider.lastConfiguration.Get(), templateObjects) {
|
||||||
log.Debugf("Skipping event from kubernetes %+v", event)
|
log.Debug("Skipping event")
|
||||||
} else {
|
} else {
|
||||||
provider.lastConfiguration.Set(templateObjects)
|
provider.lastConfiguration.Set(templateObjects)
|
||||||
configurationChan <- types.ConfigMessage{
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configuration, error) {
|
func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configuration, error) {
|
||||||
ingresses, err := k8sClient.GetIngresses(provider.LabelSelector, func(ingress k8s.Ingress) bool {
|
ingresses := k8sClient.GetIngresses(provider.Namespaces)
|
||||||
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
|
|
||||||
}
|
|
||||||
templateObjects := types.Configuration{
|
templateObjects := types.Configuration{
|
||||||
map[string]*types.Backend{},
|
map[string]*types.Backend{},
|
||||||
map[string]*types.Frontend{},
|
map[string]*types.Frontend{},
|
||||||
|
@ -239,28 +157,28 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
|
||||||
Rule: ruleType + ":" + pa.Path,
|
Rule: ruleType + ":" + pa.Path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
service, err := k8sClient.GetService(pa.Backend.ServiceName, i.ObjectMeta.Namespace)
|
service, exists, err := k8sClient.GetService(i.ObjectMeta.Namespace, pa.Backend.ServiceName)
|
||||||
if err != nil {
|
if err != nil || !exists {
|
||||||
log.Warnf("Error retrieving services: %v", err)
|
log.Warnf("Error retrieving service %s/%s: %v", i.ObjectMeta.Namespace, pa.Backend.ServiceName, err)
|
||||||
delete(templateObjects.Frontends, r.Host+pa.Path)
|
delete(templateObjects.Frontends, r.Host+pa.Path)
|
||||||
log.Warnf("Error retrieving services %s", pa.Backend.ServiceName)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol := "http"
|
protocol := "http"
|
||||||
for _, port := range service.Spec.Ports {
|
for _, port := range service.Spec.Ports {
|
||||||
if equalPorts(port, pa.Backend.ServicePort) {
|
if equalPorts(port, pa.Backend.ServicePort) {
|
||||||
if port.Port == 443 {
|
if port.Port == 443 {
|
||||||
protocol = "https"
|
protocol = "https"
|
||||||
}
|
}
|
||||||
endpoints, err := k8sClient.GetEndpoints(service.ObjectMeta.Name, service.ObjectMeta.Namespace)
|
endpoints, exists, err := k8sClient.GetEndpoints(service.ObjectMeta.Namespace, service.ObjectMeta.Name)
|
||||||
if err != nil {
|
if err != nil || !exists {
|
||||||
log.Errorf("Error retrieving endpoints: %v", err)
|
log.Errorf("Error retrieving endpoints %s/%s: %v", service.ObjectMeta.Namespace, service.ObjectMeta.Name, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(endpoints.Subsets) == 0 {
|
if len(endpoints.Subsets) == 0 {
|
||||||
log.Warnf("Endpoints not found for %s/%s, falling back to Service ClusterIP", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
|
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{
|
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,
|
Weight: 1,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -287,7 +205,7 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
|
||||||
return &templateObjects, nil
|
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 {
|
if len(endpointPorts) > 0 {
|
||||||
//name is optional if there is only one port
|
//name is optional if there is only one port
|
||||||
port := endpointPorts[0]
|
port := endpointPorts[0]
|
||||||
|
@ -298,11 +216,11 @@ func endpointPortNumber(servicePort k8s.ServicePort, endpointPorts []k8s.Endpoin
|
||||||
}
|
}
|
||||||
return int(port.Port)
|
return int(port.Port)
|
||||||
}
|
}
|
||||||
return servicePort.Port
|
return int(servicePort.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func equalPorts(servicePort k8s.ServicePort, ingressPort k8s.IntOrString) bool {
|
func equalPorts(servicePort v1.ServicePort, ingressPort intstr.IntOrString) bool {
|
||||||
if servicePort.Port == ingressPort.IntValue() {
|
if int(servicePort.Port) == ingressPort.IntValue() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if servicePort.Name != "" && servicePort.Name == ingressPort.String() {
|
if servicePort.Name != "" && servicePort.Name == ingressPort.String() {
|
||||||
|
|
|
@ -2,29 +2,34 @@ package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/containous/traefik/provider/k8s"
|
|
||||||
"github.com/containous/traefik/types"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"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) {
|
func TestLoadIngresses(t *testing.T) {
|
||||||
ingresses := []k8s.Ingress{{
|
ingresses := []*v1beta1.Ingress{{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "testing",
|
Namespace: "testing",
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo",
|
Host: "foo",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar",
|
Path: "/bar",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
ServiceName: "service1",
|
||||||
ServicePort: k8s.FromInt(80),
|
ServicePort: intstr.FromInt(80),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -33,19 +38,19 @@ func TestLoadIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Host: "bar",
|
Host: "bar",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service3",
|
ServiceName: "service3",
|
||||||
ServicePort: k8s.FromString("https"),
|
ServicePort: intstr.FromString("https"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service2",
|
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",
|
Name: "service1",
|
||||||
UID: "1",
|
UID: "1",
|
||||||
Namespace: "testing",
|
Namespace: "testing",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.1",
|
ClusterIP: "10.0.0.1",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Port: 80,
|
Port: 80,
|
||||||
},
|
},
|
||||||
|
@ -72,14 +77,14 @@ func TestLoadIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Name: "service2",
|
Name: "service2",
|
||||||
UID: "2",
|
UID: "2",
|
||||||
Namespace: "testing",
|
Namespace: "testing",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.2",
|
ClusterIP: "10.0.0.2",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Port: 802,
|
Port: 802,
|
||||||
},
|
},
|
||||||
|
@ -87,14 +92,14 @@ func TestLoadIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Name: "service3",
|
Name: "service3",
|
||||||
UID: "3",
|
UID: "3",
|
||||||
Namespace: "testing",
|
Namespace: "testing",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.3",
|
ClusterIP: "10.0.0.3",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 80,
|
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",
|
Name: "service1",
|
||||||
UID: "1",
|
UID: "1",
|
||||||
Namespace: "testing",
|
Namespace: "testing",
|
||||||
},
|
},
|
||||||
Subsets: []k8s.EndpointSubset{
|
Subsets: []v1.EndpointSubset{
|
||||||
{
|
{
|
||||||
Addresses: []k8s.EndpointAddress{
|
Addresses: []v1.EndpointAddress{
|
||||||
{
|
{
|
||||||
IP: "10.10.0.1",
|
IP: "10.10.0.1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []k8s.EndpointPort{
|
Ports: []v1.EndpointPort{
|
||||||
{
|
{
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Addresses: []k8s.EndpointAddress{
|
Addresses: []v1.EndpointAddress{
|
||||||
{
|
{
|
||||||
IP: "10.21.0.1",
|
IP: "10.21.0.1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []k8s.EndpointPort{
|
Ports: []v1.EndpointPort{
|
||||||
{
|
{
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
},
|
},
|
||||||
|
@ -142,19 +147,19 @@ func TestLoadIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Name: "service3",
|
Name: "service3",
|
||||||
UID: "3",
|
UID: "3",
|
||||||
Namespace: "testing",
|
Namespace: "testing",
|
||||||
},
|
},
|
||||||
Subsets: []k8s.EndpointSubset{
|
Subsets: []v1.EndpointSubset{
|
||||||
{
|
{
|
||||||
Addresses: []k8s.EndpointAddress{
|
Addresses: []v1.EndpointAddress{
|
||||||
{
|
{
|
||||||
IP: "10.15.0.1",
|
IP: "10.15.0.1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []k8s.EndpointPort{
|
Ports: []v1.EndpointPort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 8080,
|
Port: 8080,
|
||||||
|
@ -166,12 +171,12 @@ func TestLoadIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Addresses: []k8s.EndpointAddress{
|
Addresses: []v1.EndpointAddress{
|
||||||
{
|
{
|
||||||
IP: "10.15.0.2",
|
IP: "10.15.0.2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Ports: []k8s.EndpointPort{
|
Ports: []v1.EndpointPort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 9080,
|
Port: 9080,
|
||||||
|
@ -267,23 +272,23 @@ func TestLoadIngresses(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRuleType(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
|
Annotations: map[string]string{"traefik.frontend.rule.type": "PathPrefixStrip"}, //camel case
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo1",
|
Host: "foo1",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar1",
|
Path: "/bar1",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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
|
Annotations: map[string]string{"traefik.frontend.rule.type": "path"}, //lower case
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo1",
|
Host: "foo1",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar2",
|
Path: "/bar2",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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
|
Annotations: map[string]string{"traefik.frontend.rule.type": "PathPrefix"}, //path prefix
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo2",
|
Host: "foo2",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar1",
|
Path: "/bar1",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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
|
Annotations: map[string]string{"traefik.frontend.rule.type": "PathStrip"}, //path strip
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo2",
|
Host: "foo2",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar2",
|
Path: "/bar2",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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
|
Annotations: map[string]string{"traefik.frontend.rule.type": "PathXXStrip"}, //wrong rule
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo1",
|
Host: "foo1",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar3",
|
Path: "/bar3",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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",
|
Name: "service1",
|
||||||
UID: "1",
|
UID: "1",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.1",
|
ClusterIP: "10.0.0.1",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 801,
|
Port: 801,
|
||||||
|
@ -495,22 +500,22 @@ func TestRuleType(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetPassHostHeader(t *testing.T) {
|
func TestGetPassHostHeader(t *testing.T) {
|
||||||
ingresses := []k8s.Ingress{{
|
ingresses := []*v1beta1.Ingress{{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo",
|
Host: "foo",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar",
|
Path: "/bar",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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",
|
Name: "service1",
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
UID: "1",
|
UID: "1",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.1",
|
ClusterIP: "10.0.0.1",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 801,
|
Port: 801,
|
||||||
|
@ -587,22 +592,22 @@ func TestGetPassHostHeader(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
|
func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
|
||||||
ingresses := []k8s.Ingress{
|
ingresses := []*v1beta1.Ingress{
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo",
|
Host: "foo",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service",
|
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",
|
Name: "service",
|
||||||
UID: "1",
|
UID: "1",
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.1",
|
ClusterIP: "10.0.0.1",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 80,
|
Port: 80,
|
||||||
|
@ -631,14 +636,14 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Name: "service",
|
Name: "service",
|
||||||
UID: "2",
|
UID: "2",
|
||||||
Namespace: "not-awesome",
|
Namespace: "not-awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.2",
|
ClusterIP: "10.0.0.2",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 80,
|
Port: 80,
|
||||||
|
@ -693,23 +698,23 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadNamespacedIngresses(t *testing.T) {
|
func TestLoadNamespacedIngresses(t *testing.T) {
|
||||||
ingresses := []k8s.Ingress{
|
ingresses := []*v1beta1.Ingress{
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo",
|
Host: "foo",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar",
|
Path: "/bar",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
ServiceName: "service1",
|
||||||
ServicePort: k8s.FromInt(801),
|
ServicePort: intstr.FromInt(801),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -718,19 +723,19 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Host: "bar",
|
Host: "bar",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service3",
|
ServiceName: "service3",
|
||||||
ServicePort: k8s.FromInt(443),
|
ServicePort: intstr.FromInt(443),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service2",
|
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",
|
Namespace: "not-awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "baz",
|
Host: "baz",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/baz",
|
Path: "/baz",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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",
|
Namespace: "awesome",
|
||||||
Name: "service1",
|
Name: "service1",
|
||||||
UID: "1",
|
UID: "1",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.1",
|
ClusterIP: "10.0.0.1",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 801,
|
Port: 801,
|
||||||
|
@ -784,14 +789,14 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Name: "service1",
|
Name: "service1",
|
||||||
Namespace: "not-awesome",
|
Namespace: "not-awesome",
|
||||||
UID: "1",
|
UID: "1",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.1",
|
ClusterIP: "10.0.0.1",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 801,
|
Port: 801,
|
||||||
|
@ -800,14 +805,14 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Name: "service2",
|
Name: "service2",
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
UID: "2",
|
UID: "2",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.2",
|
ClusterIP: "10.0.0.2",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Port: 802,
|
Port: 802,
|
||||||
},
|
},
|
||||||
|
@ -815,14 +820,14 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Name: "service3",
|
Name: "service3",
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
UID: "3",
|
UID: "3",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.3",
|
ClusterIP: "10.0.0.3",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 443,
|
Port: 443,
|
||||||
|
@ -906,23 +911,23 @@ func TestLoadNamespacedIngresses(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
||||||
ingresses := []k8s.Ingress{
|
ingresses := []*v1beta1.Ingress{
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "foo",
|
Host: "foo",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar",
|
Path: "/bar",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
ServiceName: "service1",
|
||||||
ServicePort: k8s.FromInt(801),
|
ServicePort: intstr.FromInt(801),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -931,19 +936,19 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Host: "bar",
|
Host: "bar",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service3",
|
ServiceName: "service3",
|
||||||
ServicePort: k8s.FromInt(443),
|
ServicePort: intstr.FromInt(443),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service2",
|
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",
|
Namespace: "somewhat-awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "awesome",
|
Host: "awesome",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/quix",
|
Path: "/quix",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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",
|
Namespace: "not-awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
Host: "baz",
|
Host: "baz",
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/baz",
|
Path: "/baz",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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",
|
Name: "service1",
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
UID: "1",
|
UID: "1",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.1",
|
ClusterIP: "10.0.0.1",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 801,
|
Port: 801,
|
||||||
|
@ -1022,14 +1027,14 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "somewhat-awesome",
|
Namespace: "somewhat-awesome",
|
||||||
Name: "service1",
|
Name: "service1",
|
||||||
UID: "17",
|
UID: "17",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.4",
|
ClusterIP: "10.0.0.4",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 801,
|
Port: 801,
|
||||||
|
@ -1038,14 +1043,14 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
Name: "service2",
|
Name: "service2",
|
||||||
UID: "2",
|
UID: "2",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.2",
|
ClusterIP: "10.0.0.2",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Port: 802,
|
Port: 802,
|
||||||
},
|
},
|
||||||
|
@ -1053,14 +1058,14 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
Name: "service3",
|
Name: "service3",
|
||||||
UID: "3",
|
UID: "3",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.3",
|
ClusterIP: "10.0.0.3",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 443,
|
Port: 443,
|
||||||
|
@ -1167,21 +1172,21 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHostlessIngress(t *testing.T) {
|
func TestHostlessIngress(t *testing.T) {
|
||||||
ingresses := []k8s.Ingress{{
|
ingresses := []*v1beta1.Ingress{{
|
||||||
ObjectMeta: k8s.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
},
|
},
|
||||||
Spec: k8s.IngressSpec{
|
Spec: v1beta1.IngressSpec{
|
||||||
Rules: []k8s.IngressRule{
|
Rules: []v1beta1.IngressRule{
|
||||||
{
|
{
|
||||||
IngressRuleValue: k8s.IngressRuleValue{
|
IngressRuleValue: v1beta1.IngressRuleValue{
|
||||||
HTTP: &k8s.HTTPIngressRuleValue{
|
HTTP: &v1beta1.HTTPIngressRuleValue{
|
||||||
Paths: []k8s.HTTPIngressPath{
|
Paths: []v1beta1.HTTPIngressPath{
|
||||||
{
|
{
|
||||||
Path: "/bar",
|
Path: "/bar",
|
||||||
Backend: k8s.IngressBackend{
|
Backend: v1beta1.IngressBackend{
|
||||||
ServiceName: "service1",
|
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",
|
Name: "service1",
|
||||||
Namespace: "awesome",
|
Namespace: "awesome",
|
||||||
UID: "1",
|
UID: "1",
|
||||||
},
|
},
|
||||||
Spec: k8s.ServiceSpec{
|
Spec: v1.ServiceSpec{
|
||||||
ClusterIP: "10.0.0.1",
|
ClusterIP: "10.0.0.1",
|
||||||
Ports: []k8s.ServicePort{
|
Ports: []v1.ServicePort{
|
||||||
{
|
{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Port: 801,
|
Port: 801,
|
||||||
|
@ -1255,42 +1260,45 @@ func TestHostlessIngress(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientMock struct {
|
type clientMock struct {
|
||||||
ingresses []k8s.Ingress
|
ingresses []*v1beta1.Ingress
|
||||||
services []k8s.Service
|
services []*v1.Service
|
||||||
endpoints []k8s.Endpoints
|
endpoints []*v1.Endpoints
|
||||||
watchChan chan interface{}
|
watchChan chan interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c clientMock) GetIngresses(labelString string, predicate func(k8s.Ingress) bool) ([]k8s.Ingress, error) {
|
func (c clientMock) GetIngresses(namespaces k8s.Namespaces) []*v1beta1.Ingress {
|
||||||
var ingresses []k8s.Ingress
|
result := make([]*v1beta1.Ingress, 0, len(c.ingresses))
|
||||||
|
|
||||||
for _, ingress := range c.ingresses {
|
for _, ingress := range c.ingresses {
|
||||||
if predicate(ingress) {
|
if k8s.HasNamespace(ingress, namespaces) {
|
||||||
ingresses = append(ingresses, ingress)
|
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 {
|
for _, service := range c.services {
|
||||||
if service.Namespace == namespace && service.Name == name {
|
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 {
|
for _, endpoints := range c.endpoints {
|
||||||
if endpoints.Namespace == namespace && endpoints.Name == name {
|
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) {
|
func (c clientMock) WatchAll(labelString string, stopCh <-chan bool) (chan interface{}, error) {
|
||||||
return c.watchChan, make(chan error), nil
|
return c.watchChan, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
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
|
go generate
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"github.com/containous/traefik/cluster"
|
"github.com/containous/traefik/cluster"
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/middlewares"
|
"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/types"
|
||||||
"github.com/containous/traefik/version"
|
"github.com/containous/traefik/version"
|
||||||
"github.com/docker/libkv/store"
|
"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(EntryPoints{}), &EntryPoints{})
|
||||||
f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{})
|
f.AddParser(reflect.TypeOf(DefaultEntryPoints{}), &DefaultEntryPoints{})
|
||||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
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{})
|
f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{})
|
||||||
|
|
||||||
//add commands
|
//add commands
|
||||||
|
|
Loading…
Reference in a new issue