Merge pull request #1607 from tariq1890/add_vs

add new source for istio virtual services
This commit is contained in:
Kubernetes Prow Robot 2020-07-01 00:46:17 -07:00 committed by GitHub
commit a11414fa3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 2102 additions and 56 deletions

View File

@ -1,4 +1,4 @@
# Configuring ExternalDNS to use the Istio Gateway Source
# Configuring ExternalDNS to use the Istio Gateway and/or Istio Virtual Service Source
This tutorial describes how to configure ExternalDNS to use the Istio Gateway source.
It is meant to supplement the other provider-specific setup tutorials.
@ -32,7 +32,8 @@ spec:
args:
- --source=service
- --source=ingress
- --source=istio-gateway
- --source=istio-gateway # choose one
- --source=istio-virtualservice # or both
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
- --provider=aws
- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
@ -63,7 +64,7 @@ rules:
resources: ["nodes"]
verbs: ["list"]
- apiGroups: ["networking.istio.io"]
resources: ["gateways"]
resources: ["gateways", "virtualservices"]
verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1
@ -102,6 +103,7 @@ spec:
- --source=service
- --source=ingress
- --source=istio-gateway
- --source=istio-virtualservice
- --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
- --provider=aws
- --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
@ -130,7 +132,7 @@ kubectl patch clusterrole external-dns --type='json' \
-p='[{"op": "add", "path": "/rules/4", "value": { "apiGroups": [ "networking.istio.io"], "resources": ["gateways"],"verbs": ["get", "watch", "list" ]} }]'
```
### Verify ExternalDNS works (Gateway example)
### Verify that Istio Gateway/VirtualService Source works
Follow the [Istio ingress traffic tutorial](https://istio.io/docs/tasks/traffic-management/ingress/)
to deploy a sample service that will be exposed outside of the service mesh.
@ -147,7 +149,8 @@ Otherwise:
$ kubectl apply -f <(istioctl kube-inject -f https://raw.githubusercontent.com/istio/istio/release-1.6/samples/httpbin/httpbin.yaml)
```
#### Create an Istio Gateway:
#### Using a Gateway as a source
##### Create an Istio Gateway:
```bash
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
@ -163,11 +166,11 @@ spec:
name: http
protocol: HTTP
hosts:
- "httpbin.example.com"
- "httpbin.example.com" # this is used by external-dns to extract DNS names
EOF
```
#### Configure routes for traffic entering via the Gateway:
##### Configure routes for traffic entering via the Gateway:
```bash
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
@ -178,7 +181,56 @@ spec:
hosts:
- "httpbin.example.com"
gateways:
- httpbin-gateway
- istio-system/httpbin-gateway
http:
- match:
- uri:
prefix: /status
- uri:
prefix: /delay
route:
- destination:
port:
number: 8000
host: httpbin
EOF
```
#### Using a VirtualService as a source
##### Create an Istio Gateway:
```bash
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
EOF
```
##### Configure routes for traffic entering via the Gateway:
```bash
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "httpbin.example.com" # this is used by external-dns to extract DNS names
gateways:
- istio-system/httpbin-gateway
http:
- match:
- uri:

View File

@ -21,6 +21,7 @@ import (
"time"
"github.com/stretchr/testify/mock"
"sigs.k8s.io/external-dns/endpoint"
)
@ -44,7 +45,7 @@ func (m *MockSource) Endpoints() ([]*endpoint.Endpoint, error) {
// AddEventHandler adds an event handler that should be triggered if something in source changes
func (m *MockSource) AddEventHandler(ctx context.Context, handler func()) {
go func() {
ticker := time.NewTicker(time.Second)
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {

View File

@ -299,7 +299,7 @@ func (cfg *Config) ParseFlags(args []string) error {
app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(source.DefaultRoutegroupVersion).StringVar(&cfg.SkipperRouteGroupVersion)
// Flags related to processing sources
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, istio-gateway, cloudfoundry, contour-ingressroute, crd, empty, skipper-routegroup,openshift-route)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "istio-gateway", "cloudfoundry", "contour-ingressroute", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route")
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, fake, connector, istio-gateway, istio-virtualservice, cloudfoundry, contour-ingressroute, crd, empty, skipper-routegroup,openshift-route)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route")
app.Flag("namespace", "Limit sources of endpoints to a specific namespace (default: all namespaces)").Default(defaultConfig.Namespace).StringVar(&cfg.Namespace)
app.Flag("annotation-filter", "Filter sources managed by external-dns via annotation using label selector semantics (default: all sources)").Default(defaultConfig.AnnotationFilter).StringVar(&cfg.AnnotationFilter)

View File

@ -180,10 +180,12 @@ func (sc *gatewaySource) Endpoints() ([]*endpoint.Endpoint, error) {
return endpoints, nil
}
// TODO(tariq1890): Implement this once we have evaluated and tested GatewayInformers
// AddEventHandler adds an event handler that should be triggered if the watched Istio Gateway changes.
func (sc *gatewaySource) AddEventHandler(ctx context.Context, handler func()) {
}
// filterByAnnotations2 filters a list of configs by a given annotation selector.
// filterByAnnotations filters a list of configs by a given annotation selector.
func (sc *gatewaySource) filterByAnnotations(gateways []networkingv1alpha3.Gateway) ([]networkingv1alpha3.Gateway, error) {
labelSelector, err := metav1.ParseToLabelSelector(sc.annotationFilter)
if err != nil {
@ -220,23 +222,23 @@ func (sc *gatewaySource) setResourceLabel(gateway networkingv1alpha3.Gateway, en
}
}
func (sc *gatewaySource) targetsFromGatewayConfig(gateway networkingv1alpha3.Gateway) (targets endpoint.Targets, err error) {
labelSelector, err := metav1.ParseToLabelSelector(labels.Set(gateway.Spec.Selector).String())
if err != nil {
return nil, err
}
selector, err := metav1.LabelSelectorAsSelector(labelSelector)
if err != nil {
return nil, err
func (sc *gatewaySource) targetsFromGateway(gateway networkingv1alpha3.Gateway) (targets endpoint.Targets, err error) {
targets = getTargetsFromTargetAnnotation(gateway.Annotations)
if len(targets) > 0 {
return
}
services, err := sc.serviceInformer.Lister().Services(sc.namespace).List(selector)
services, err := sc.serviceInformer.Lister().Services(sc.namespace).List(labels.Everything())
if err != nil {
log.Error(err)
return
}
for _, service := range services {
if !gatewaySelectorMatchesServiceSelector(gateway.Spec.Selector, service.Spec.Selector) {
continue
}
for _, lb := range service.Status.LoadBalancer.Ingress {
if lb.IP != "" {
targets = append(targets, lb.IP)
@ -262,7 +264,7 @@ func (sc *gatewaySource) endpointsFromGateway(hostnames []string, gateway networ
targets := getTargetsFromTargetAnnotation(annotations)
if len(targets) == 0 {
targets, err = sc.targetsFromGatewayConfig(gateway)
targets, err = sc.targetsFromGateway(gateway)
if err != nil {
return nil, err
}
@ -315,3 +317,12 @@ func (sc *gatewaySource) hostNamesFromTemplate(gateway networkingv1alpha3.Gatewa
hostnames := strings.Split(strings.Replace(buf.String(), " ", "", -1), ",")
return hostnames, nil
}
func gatewaySelectorMatchesServiceSelector(gwSelector, svcSelector map[string]string) bool {
for k, v := range gwSelector {
if lbl, ok := svcSelector[k]; !ok || lbl != v {
return false
}
}
return true
}

View File

@ -1151,6 +1151,7 @@ type fakeIngressGatewayService struct {
hostnames []string
namespace string
name string
selector map[string]string
}
func (ig fakeIngressGatewayService) Service() *v1.Service {
@ -1164,6 +1165,9 @@ func (ig fakeIngressGatewayService) Service() *v1.Service {
Ingress: []v1.LoadBalancerIngress{},
},
},
Spec: v1.ServiceSpec{
Selector: ig.selector,
},
}
for _, ip := range ig.ips {

View File

@ -17,15 +17,12 @@ limitations under the License.
package source
import (
"context"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/internal/testutils"
)
func TestGetTTLFromAnnotations(t *testing.T) {
@ -108,35 +105,3 @@ func TestSuitableType(t *testing.T) {
}
}
}
// TestSourceEventHandler that AddEventHandler calls provided handler
func TestSourceEventHandler(t *testing.T) {
source := new(testutils.MockSource)
handlerCh := make(chan bool)
ctx, cancel := context.WithCancel(context.Background())
// Define and register a simple handler that sends a message to a channel to show it was called.
handler := func() {
handlerCh <- true
}
// Example of preventing handler from being called more than once every 5 seconds.
source.AddEventHandler(ctx, handler)
// Send timeout message after 10 seconds to fail test if handler is not called.
go func() {
time.Sleep(10 * time.Second)
cancel()
}()
// Wait until we either receive a message from handlerCh or timeoutCh channel after 10 seconds.
select {
case msg := <-handlerCh:
assert.True(t, msg)
case <-ctx.Done():
assert.Fail(t, "timed out waiting for event handler to be called")
}
close(handlerCh)
}

View File

@ -195,6 +195,16 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
return nil, err
}
return NewIstioGatewaySource(kubernetesClient, istioClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
case "istio-virtualservice":
kubernetesClient, err := p.KubeClient()
if err != nil {
return nil, err
}
istioClient, err := p.IstioClient()
if err != nil {
return nil, err
}
return NewIstioVirtualServiceSource(kubernetesClient, istioClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
case "cloudfoundry":
cfClient, err := p.CloudFoundryClient(cfg.CFAPIEndpoint, cfg.CFUsername, cfg.CFPassword)
if err != nil {

449
source/virtualservice.go Normal file
View File

@ -0,0 +1,449 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package source
import (
"bytes"
"context"
"fmt"
"sort"
"strings"
"text/template"
"time"
networkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
log "github.com/sirupsen/logrus"
istioclient "istio.io/client-go/pkg/clientset/versioned"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/wait"
kubeinformers "k8s.io/client-go/informers"
coreinformers "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"sigs.k8s.io/external-dns/endpoint"
)
// IstioMeshGateway is the built in gateway for all sidecars
const IstioMeshGateway = "mesh"
// virtualServiceSource is an implementation of Source for Istio VirtualService objects.
// The implementation uses the spec.hosts values for the hostnames.
// Use targetAnnotationKey to explicitly set Endpoint.
type virtualServiceSource struct {
kubeClient kubernetes.Interface
istioClient istioclient.Interface
namespace string
annotationFilter string
fqdnTemplate *template.Template
combineFQDNAnnotation bool
ignoreHostnameAnnotation bool
serviceInformer coreinformers.ServiceInformer
}
// NewIstioVirtualServiceSource creates a new virtualServiceSource with the given config.
func NewIstioVirtualServiceSource(
kubeClient kubernetes.Interface,
istioClient istioclient.Interface,
namespace string,
annotationFilter string,
fqdnTemplate string,
combineFQDNAnnotation bool,
ignoreHostnameAnnotation bool,
) (Source, error) {
var (
tmpl *template.Template
err error
)
if fqdnTemplate != "" {
tmpl, err = template.New("endpoint").Funcs(template.FuncMap{
"trimPrefix": strings.TrimPrefix,
}).Parse(fqdnTemplate)
if err != nil {
return nil, err
}
}
// Use shared informers to listen for add/update/delete of services/pods/nodes in the specified namespace.
// Set resync period to 0, to prevent processing when nothing has changed
informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace))
serviceInformer := informerFactory.Core().V1().Services()
// Add default resource event handlers to properly initialize informer.
serviceInformer.Informer().AddEventHandler(
cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
log.Debug("service added")
},
},
)
// TODO informer is not explicitly stopped since controller is not passing in its channel.
informerFactory.Start(wait.NeverStop)
// wait for the local cache to be populated.
err = wait.Poll(time.Second, 60*time.Second, func() (bool, error) {
return serviceInformer.Informer().HasSynced(), nil
})
if err != nil {
return nil, fmt.Errorf("failed to sync cache: %v", err)
}
return &virtualServiceSource{
kubeClient: kubeClient,
istioClient: istioClient,
namespace: namespace,
annotationFilter: annotationFilter,
fqdnTemplate: tmpl,
combineFQDNAnnotation: combineFQDNAnnotation,
ignoreHostnameAnnotation: ignoreHostnameAnnotation,
serviceInformer: serviceInformer,
}, nil
}
// Endpoints returns endpoint objects for each host-target combination that should be processed.
// Retrieves all VirtualService resources in the source's namespace(s).
func (sc *virtualServiceSource) Endpoints() ([]*endpoint.Endpoint, error) {
virtualServiceList, err := sc.istioClient.NetworkingV1alpha3().VirtualServices(sc.namespace).List(metav1.ListOptions{})
if err != nil {
return nil, err
}
virtualServices := virtualServiceList.Items
virtualServices, err = sc.filterByAnnotations(virtualServices)
if err != nil {
return nil, err
}
var endpoints []*endpoint.Endpoint
for _, virtualService := range virtualServices {
// Check controller annotation to see if we are responsible.
controller, ok := virtualService.Annotations[controllerAnnotationKey]
if ok && controller != controllerAnnotationValue {
log.Debugf("Skipping VirtualService %s/%s because controller value does not match, found: %s, required: %s",
virtualService.Namespace, virtualService.Name, controller, controllerAnnotationValue)
continue
}
gwEndpoints, err := sc.endpointsFromVirtualService(virtualService)
if err != nil {
return nil, err
}
// apply template if host is missing on VirtualService
if (sc.combineFQDNAnnotation || len(gwEndpoints) == 0) && sc.fqdnTemplate != nil {
iEndpoints, err := sc.endpointsFromTemplate(virtualService)
if err != nil {
return nil, err
}
if sc.combineFQDNAnnotation {
gwEndpoints = append(gwEndpoints, iEndpoints...)
} else {
gwEndpoints = iEndpoints
}
}
if len(gwEndpoints) == 0 {
log.Debugf("No endpoints could be generated from VirtualService %s/%s", virtualService.Namespace, virtualService.Name)
continue
}
log.Debugf("Endpoints generated from VirtualService: %s/%s: %v", virtualService.Namespace, virtualService.Name, gwEndpoints)
sc.setResourceLabel(virtualService, gwEndpoints)
endpoints = append(endpoints, gwEndpoints...)
}
for _, ep := range endpoints {
sort.Sort(ep.Targets)
}
return endpoints, nil
}
// TODO(tariq1890): Implement this once we have evaluated and tested VirtualServiceInformers
// AddEventHandler adds an event handler that should be triggered if the watched Istio VirtualService changes.
func (sc *virtualServiceSource) AddEventHandler(ctx context.Context, handler func()) {
}
func (sc *virtualServiceSource) getGateway(gatewayStr string, virtualService networkingv1alpha3.VirtualService) *networkingv1alpha3.Gateway {
if gatewayStr == "" || gatewayStr == IstioMeshGateway {
// This refers to "all sidecars in the mesh"; ignore.
return nil
}
namespace, name, err := parseGateway(gatewayStr)
if err != nil {
log.Debugf("Failed parsing gatewayStr %s of VirtualService %s/%s", gatewayStr, virtualService.Namespace, virtualService.Name)
return nil
}
if namespace == "" {
namespace = virtualService.Namespace
}
gateway, err := sc.istioClient.NetworkingV1alpha3().Gateways(namespace).Get(name, metav1.GetOptions{})
if err != nil {
log.Errorf("Failed retrieving gateway %s referenced by VirtualService %s/%s: %v", gatewayStr, virtualService.Namespace, virtualService.Name, err)
return nil
}
if gateway == nil {
log.Debugf("Gateway %s referenced by VirtualService %s/%s not found: %v", gatewayStr, virtualService.Namespace, virtualService.Name, err)
return nil
}
return gateway
}
func (sc *virtualServiceSource) endpointsFromTemplate(virtualService networkingv1alpha3.VirtualService) ([]*endpoint.Endpoint, error) {
// Process the whole template string
var buf bytes.Buffer
err := sc.fqdnTemplate.Execute(&buf, virtualService)
if err != nil {
return nil, fmt.Errorf("failed to apply template on istio config %v: %v", virtualService, err)
}
hostnamesTemplate := buf.String()
ttl, err := getTTLFromAnnotations(virtualService.Annotations)
if err != nil {
log.Warn(err)
}
var endpoints []*endpoint.Endpoint
providerSpecific, setIdentifier := getProviderSpecificAnnotations(virtualService.Annotations)
// splits the FQDN template and removes the trailing periods
hostnames := strings.Split(strings.Replace(hostnamesTemplate, " ", "", -1), ",")
for _, hostname := range hostnames {
hostname = strings.TrimSuffix(hostname, ".")
targets, err := sc.targetsFromVirtualService(virtualService, hostname)
if err != nil {
return endpoints, err
}
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...)
}
return endpoints, nil
}
// filterByAnnotations filters a list of configs by a given annotation selector.
func (sc *virtualServiceSource) filterByAnnotations(virtualservices []networkingv1alpha3.VirtualService) ([]networkingv1alpha3.VirtualService, error) {
labelSelector, err := metav1.ParseToLabelSelector(sc.annotationFilter)
if err != nil {
return nil, err
}
selector, err := metav1.LabelSelectorAsSelector(labelSelector)
if err != nil {
return nil, err
}
// empty filter returns original list
if selector.Empty() {
return virtualservices, nil
}
var filteredList []networkingv1alpha3.VirtualService
for _, virtualservice := range virtualservices {
// convert the annotations to an equivalent label selector
annotations := labels.Set(virtualservice.Annotations)
// include if the annotations match the selector
if selector.Matches(annotations) {
filteredList = append(filteredList, virtualservice)
}
}
return filteredList, nil
}
func (sc *virtualServiceSource) setResourceLabel(virtualservice networkingv1alpha3.VirtualService, endpoints []*endpoint.Endpoint) {
for _, ep := range endpoints {
ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("virtualservice/%s/%s", virtualservice.Namespace, virtualservice.Name)
}
}
func (sc *virtualServiceSource) targetsFromVirtualService(virtualService networkingv1alpha3.VirtualService, vsHost string) ([]string, error) {
var targets []string
// for each host we need to iterate through the gateways because each host might match for only one of the gateways
for _, gateway := range virtualService.Spec.Gateways {
gateway := sc.getGateway(gateway, virtualService)
if gateway == nil {
continue
}
if !virtualServiceBindsToGateway(&virtualService, gateway, vsHost) {
continue
}
tgs, err := sc.targetsFromGateway(gateway)
if err != nil {
return targets, err
}
targets = append(targets, tgs...)
}
return targets, nil
}
// endpointsFromVirtualService extracts the endpoints from an Istio VirtualService Config object
func (sc *virtualServiceSource) endpointsFromVirtualService(virtualservice networkingv1alpha3.VirtualService) ([]*endpoint.Endpoint, error) {
var endpoints []*endpoint.Endpoint
ttl, err := getTTLFromAnnotations(virtualservice.Annotations)
if err != nil {
log.Warn(err)
}
targetsFromAnnotation := getTargetsFromTargetAnnotation(virtualservice.Annotations)
providerSpecific, setIdentifier := getProviderSpecificAnnotations(virtualservice.Annotations)
for _, host := range virtualservice.Spec.Hosts {
if host == "" || host == "*" {
continue
}
parts := strings.Split(host, "/")
// If the input hostname is of the form my-namespace/foo.bar.com, remove the namespace
// before appending it to the list of endpoints to create
if len(parts) == 2 {
host = parts[1]
}
targets := targetsFromAnnotation
if len(targets) == 0 {
targets, err = sc.targetsFromVirtualService(virtualservice, host)
if err != nil {
return endpoints, err
}
}
endpoints = append(endpoints, endpointsForHostname(host, targets, ttl, providerSpecific, setIdentifier)...)
}
// Skip endpoints if we do not want entries from annotations
if !sc.ignoreHostnameAnnotation {
hostnameList := getHostnamesFromAnnotations(virtualservice.Annotations)
for _, hostname := range hostnameList {
targets := targetsFromAnnotation
if len(targets) == 0 {
targets, err = sc.targetsFromVirtualService(virtualservice, hostname)
if err != nil {
return endpoints, err
}
}
endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier)...)
}
}
return endpoints, nil
}
// checks if the given VirtualService should actually bind to the given gateway
// see requirements here: https://istio.io/docs/reference/config/networking/gateway/#Server
func virtualServiceBindsToGateway(virtualService *networkingv1alpha3.VirtualService, gateway *networkingv1alpha3.Gateway, vsHost string) bool {
isValid := false
if len(virtualService.Spec.ExportTo) == 0 {
isValid = true
} else {
for _, ns := range virtualService.Spec.ExportTo {
if ns == "*" || ns == gateway.Namespace || (ns == "." && gateway.Namespace == virtualService.Namespace) {
isValid = true
}
}
}
if !isValid {
return false
}
for _, server := range gateway.Spec.Servers {
for _, host := range server.Hosts {
namespace := "*"
parts := strings.Split(host, "/")
if len(parts) == 2 {
namespace = parts[0]
host = parts[1]
} else if len(parts) != 1 {
log.Debugf("Gateway %s/%s has invalid host %s", gateway.Namespace, gateway.Name, host)
continue
}
if namespace == "*" || namespace == virtualService.Namespace || (namespace == "." && virtualService.Namespace == gateway.Namespace) {
if host == "*" {
return true
}
suffixMatch := false
if strings.HasPrefix(host, "*.") {
suffixMatch = true
}
if host == vsHost || (suffixMatch && strings.HasSuffix(vsHost, host[1:])) {
return true
}
}
}
}
return false
}
func parseGateway(gateway string) (namespace, name string, err error) {
parts := strings.Split(gateway, "/")
if len(parts) == 2 {
namespace, name = parts[0], parts[1]
} else if len(parts) == 1 {
name = parts[0]
} else {
err = fmt.Errorf("invalid gateway name (name or namespace/name) found '%v'", gateway)
}
return
}
func (sc *virtualServiceSource) targetsFromGateway(gateway *networkingv1alpha3.Gateway) (targets endpoint.Targets, err error) {
targets = getTargetsFromTargetAnnotation(gateway.Annotations)
if len(targets) > 0 {
return
}
services, err := sc.serviceInformer.Lister().Services(sc.namespace).List(labels.Everything())
if err != nil {
log.Error(err)
return
}
for _, service := range services {
if !gatewaySelectorMatchesServiceSelector(gateway.Spec.Selector, service.Spec.Selector) {
continue
}
for _, lb := range service.Status.LoadBalancer.Ingress {
if lb.IP != "" {
targets = append(targets, lb.IP)
} else if lb.Hostname != "" {
targets = append(targets, lb.Hostname)
}
}
}
return
}

File diff suppressed because it is too large Load Diff