mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36:58 +02:00
Merge pull request #1693 from LuxChanLu/gloo-proxy
feat: add gloo proxy source
This commit is contained in:
commit
030d86c201
101
docs/tutorials/gloo-proxy.md
Normal file
101
docs/tutorials/gloo-proxy.md
Normal file
@ -0,0 +1,101 @@
|
||||
# Configuring ExternalDNS to use the Gloo Proxy Source
|
||||
This tutorial describes how to configure ExternalDNS to use the Gloo Proxy source.
|
||||
It is meant to supplement the other provider-specific setup tutorials.
|
||||
|
||||
### Manifest (for clusters without RBAC enabled)
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
args:
|
||||
- --source=gloo-proxy
|
||||
- --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system)
|
||||
- --provider=aws
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
```
|
||||
|
||||
### Manifest (for clusters with RBAC enabled)
|
||||
Could be change if you have mulitple sources
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: external-dns
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: external-dns
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["services","endpoints","pods"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
- apiGroups: ["gloo.solo.io"]
|
||||
resources: ["proxies"]
|
||||
verbs: ["get","watch","list"]
|
||||
- apiGroups: ["gateway.solo.io"]
|
||||
resources: ["virtualservices"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: external-dns-viewer
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: external-dns
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: external-dns
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: external-dns
|
||||
spec:
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: external-dns
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: external-dns
|
||||
spec:
|
||||
serviceAccountName: external-dns
|
||||
containers:
|
||||
- name: external-dns
|
||||
# update this to the desired external-dns version
|
||||
image: k8s.gcr.io/external-dns/external-dns:v0.7.6
|
||||
args:
|
||||
- --source=gloo-proxy
|
||||
- --gloo-namespace=custom-gloo-system # gloo system namespace. Omit to use the default (gloo-system)
|
||||
- --provider=aws
|
||||
- --registry=txt
|
||||
- --txt-owner-id=my-identifier
|
||||
```
|
||||
|
1
main.go
1
main.go
@ -121,6 +121,7 @@ func main() {
|
||||
CFUsername: cfg.CFUsername,
|
||||
CFPassword: cfg.CFPassword,
|
||||
ContourLoadBalancerService: cfg.ContourLoadBalancerService,
|
||||
GlooNamespace: cfg.GlooNamespace,
|
||||
SkipperRouteGroupVersion: cfg.SkipperRouteGroupVersion,
|
||||
RequestTimeout: cfg.RequestTimeout,
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ type Config struct {
|
||||
KubeConfig string
|
||||
RequestTimeout time.Duration
|
||||
ContourLoadBalancerService string
|
||||
GlooNamespace string
|
||||
SkipperRouteGroupVersion string
|
||||
Sources []string
|
||||
Namespace string
|
||||
@ -169,6 +170,7 @@ var defaultConfig = &Config{
|
||||
KubeConfig: "",
|
||||
RequestTimeout: time.Second * 30,
|
||||
ContourLoadBalancerService: "heptio-contour/contour",
|
||||
GlooNamespace: "gloo-system",
|
||||
SkipperRouteGroupVersion: "zalando.org/v1",
|
||||
Sources: nil,
|
||||
Namespace: "",
|
||||
@ -332,12 +334,14 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
// Flags related to Contour
|
||||
app.Flag("contour-load-balancer", "The fully-qualified name of the Contour load balancer service. (default: heptio-contour/contour)").Default("heptio-contour/contour").StringVar(&cfg.ContourLoadBalancerService)
|
||||
|
||||
// Flags related to Gloo
|
||||
app.Flag("gloo-namespace", "Gloo namespace. (default: gloo-system)").Default("gloo-system").StringVar(&cfg.GlooNamespace)
|
||||
|
||||
// Flags related to Skipper RouteGroup
|
||||
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, istio-virtualservice, cloudfoundry, contour-ingressroute, contour-httpproxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host")
|
||||
|
||||
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, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-ingressroute", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host")
|
||||
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)
|
||||
app.Flag("label-filter", "Filter sources managed by external-dns via label selector when listing all resources; currently only supported by source CRD").Default(defaultConfig.LabelFilter).StringVar(&cfg.LabelFilter)
|
||||
|
@ -35,6 +35,7 @@ var (
|
||||
KubeConfig: "",
|
||||
RequestTimeout: time.Second * 30,
|
||||
ContourLoadBalancerService: "heptio-contour/contour",
|
||||
GlooNamespace: "gloo-system",
|
||||
SkipperRouteGroupVersion: "zalando.org/v1",
|
||||
Sources: []string{"service"},
|
||||
Namespace: "",
|
||||
@ -115,6 +116,7 @@ var (
|
||||
KubeConfig: "/some/path",
|
||||
RequestTimeout: time.Second * 77,
|
||||
ContourLoadBalancerService: "heptio-contour-other/contour-other",
|
||||
GlooNamespace: "gloo-not-system",
|
||||
SkipperRouteGroupVersion: "zalando.org/v2",
|
||||
Sources: []string{"service", "ingress", "connector"},
|
||||
Namespace: "namespace",
|
||||
@ -222,6 +224,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"--kubeconfig=/some/path",
|
||||
"--request-timeout=77s",
|
||||
"--contour-load-balancer=heptio-contour-other/contour-other",
|
||||
"--gloo-namespace=gloo-not-system",
|
||||
"--skipper-routegroup-groupversion=zalando.org/v2",
|
||||
"--source=service",
|
||||
"--source=ingress",
|
||||
@ -320,6 +323,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"EXTERNAL_DNS_KUBECONFIG": "/some/path",
|
||||
"EXTERNAL_DNS_REQUEST_TIMEOUT": "77s",
|
||||
"EXTERNAL_DNS_CONTOUR_LOAD_BALANCER": "heptio-contour-other/contour-other",
|
||||
"EXTERNAL_DNS_GLOO_NAMESPACE": "gloo-not-system",
|
||||
"EXTERNAL_DNS_SKIPPER_ROUTEGROUP_GROUPVERSION": "zalando.org/v2",
|
||||
"EXTERNAL_DNS_SOURCE": "service\ningress\nconnector",
|
||||
"EXTERNAL_DNS_NAMESPACE": "namespace",
|
||||
|
200
source/gloo.go
Normal file
200
source/gloo.go
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
Copyright 2020n 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 (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
var (
|
||||
proxyGVR = schema.GroupVersionResource{
|
||||
Group: "gloo.solo.io",
|
||||
Version: "v1",
|
||||
Resource: "proxies",
|
||||
}
|
||||
virtualServiceGVR = schema.GroupVersionResource{
|
||||
Group: "gateway.solo.io",
|
||||
Version: "v1",
|
||||
Resource: "virtualservices",
|
||||
}
|
||||
)
|
||||
|
||||
// Basic redefinition of "Proxy" CRD : https://github.com/solo-io/gloo/blob/v1.4.6/projects/gloo/pkg/api/v1/proxy.pb.go
|
||||
type proxy struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
Metadata metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
Spec proxySpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
type proxySpec struct {
|
||||
Listeners []proxySpecListener `json:"listeners,omitempty"`
|
||||
}
|
||||
|
||||
type proxySpecListener struct {
|
||||
HTTPListener proxySpecHTTPListener `json:"httpListener,omitempty"`
|
||||
}
|
||||
|
||||
type proxySpecHTTPListener struct {
|
||||
VirtualHosts []proxyVirtualHost `json:"virtualHosts,omitempty"`
|
||||
}
|
||||
|
||||
type proxyVirtualHost struct {
|
||||
Domains []string `json:"domains,omitempty"`
|
||||
Metadata proxyVirtualHostMetadata `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type proxyVirtualHostMetadata struct {
|
||||
Source []proxyVirtualHostMetadataSource `json:"sources,omitempty"`
|
||||
}
|
||||
|
||||
type proxyVirtualHostMetadataSource struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
type glooSource struct {
|
||||
dynamicKubeClient dynamic.Interface
|
||||
kubeClient kubernetes.Interface
|
||||
glooNamespace string
|
||||
}
|
||||
|
||||
// NewGlooSource creates a new glooSource with the given config
|
||||
func NewGlooSource(dynamicKubeClient dynamic.Interface, kubeClient kubernetes.Interface, glooNamespace string) (Source, error) {
|
||||
return &glooSource{
|
||||
dynamicKubeClient,
|
||||
kubeClient,
|
||||
glooNamespace,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (gs *glooSource) AddEventHandler(ctx context.Context, handler func()) {
|
||||
}
|
||||
|
||||
// Endpoints returns endpoint objects
|
||||
func (gs *glooSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
||||
endpoints := []*endpoint.Endpoint{}
|
||||
|
||||
proxies, err := gs.dynamicKubeClient.Resource(proxyGVR).Namespace(gs.glooNamespace).List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, obj := range proxies.Items {
|
||||
proxy := proxy{}
|
||||
jsonString, err := obj.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(jsonString, &proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Gloo: Find %s proxy", proxy.Metadata.Name)
|
||||
proxyTargets, err := gs.proxyTargets(ctx, proxy.Metadata.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Gloo[%s]: Find %d target(s) (%+v)", proxy.Metadata.Name, len(proxyTargets), proxyTargets)
|
||||
proxyEndpoints, err := gs.generateEndpointsFromProxy(ctx, &proxy, proxyTargets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Gloo[%s]: Generate %d endpoint(s)", proxy.Metadata.Name, len(proxyEndpoints))
|
||||
endpoints = append(endpoints, proxyEndpoints...)
|
||||
}
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
func (gs *glooSource) generateEndpointsFromProxy(ctx context.Context, proxy *proxy, targets endpoint.Targets) ([]*endpoint.Endpoint, error) {
|
||||
endpoints := []*endpoint.Endpoint{}
|
||||
for _, listener := range proxy.Spec.Listeners {
|
||||
for _, virtualHost := range listener.HTTPListener.VirtualHosts {
|
||||
annotations, err := gs.annotationsFromProxySource(ctx, virtualHost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ttl, err := getTTLFromAnnotations(annotations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
providerSpecific, setIdentifier := getProviderSpecificAnnotations(annotations)
|
||||
for _, domain := range virtualHost.Domains {
|
||||
endpoints = append(endpoints, endpointsForHostname(strings.TrimSuffix(domain, "."), targets, ttl, providerSpecific, setIdentifier)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
func (gs *glooSource) annotationsFromProxySource(ctx context.Context, virtualHost proxyVirtualHost) (map[string]string, error) {
|
||||
annotations := map[string]string{}
|
||||
for _, src := range virtualHost.Metadata.Source {
|
||||
kind := sourceKind(src.Kind)
|
||||
if kind != nil {
|
||||
source, err := gs.dynamicKubeClient.Resource(*kind).Namespace(src.Namespace).Get(ctx, src.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for key, value := range source.GetAnnotations() {
|
||||
annotations[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
return annotations, nil
|
||||
}
|
||||
|
||||
func (gs *glooSource) proxyTargets(ctx context.Context, name string) (endpoint.Targets, error) {
|
||||
svc, err := gs.kubeClient.CoreV1().Services(gs.glooNamespace).Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var targets endpoint.Targets
|
||||
switch svc.Spec.Type {
|
||||
case corev1.ServiceTypeLoadBalancer:
|
||||
for _, lb := range svc.Status.LoadBalancer.Ingress {
|
||||
if lb.IP != "" {
|
||||
targets = append(targets, lb.IP)
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
targets = append(targets, lb.Hostname)
|
||||
}
|
||||
}
|
||||
default:
|
||||
log.WithField("gateway", name).WithField("service", svc).Warn("Gloo: Proxy service type not supported")
|
||||
}
|
||||
return targets, nil
|
||||
}
|
||||
|
||||
func sourceKind(kind string) *schema.GroupVersionResource {
|
||||
switch kind {
|
||||
case "*v1.VirtualService":
|
||||
return &virtualServiceGVR
|
||||
}
|
||||
return nil
|
||||
}
|
320
source/gloo_test.go
Normal file
320
source/gloo_test.go
Normal file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
Copyright 2020n 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 (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
fakeDynamic "k8s.io/client-go/dynamic/fake"
|
||||
fakeKube "k8s.io/client-go/kubernetes/fake"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
)
|
||||
|
||||
// This is a compile-time validation that glooSource is a Source.
|
||||
var _ Source = &glooSource{}
|
||||
|
||||
const defaultGlooNamespace = "gloo-system"
|
||||
|
||||
// Internal proxy test
|
||||
var internalProxy = proxy{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: proxyGVR.GroupVersion().String(),
|
||||
Kind: "Proxy",
|
||||
},
|
||||
Metadata: metav1.ObjectMeta{
|
||||
Name: "internal",
|
||||
Namespace: defaultGlooNamespace,
|
||||
},
|
||||
Spec: proxySpec{
|
||||
Listeners: []proxySpecListener{
|
||||
{
|
||||
HTTPListener: proxySpecHTTPListener{
|
||||
VirtualHosts: []proxyVirtualHost{
|
||||
{
|
||||
Domains: []string{"a.test", "b.test"},
|
||||
Metadata: proxyVirtualHostMetadata{
|
||||
Source: []proxyVirtualHostMetadataSource{
|
||||
{
|
||||
Kind: "*v1.Unknown",
|
||||
Name: "my-unknown-svc",
|
||||
Namespace: "unknown",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: []string{"c.test"},
|
||||
Metadata: proxyVirtualHostMetadata{
|
||||
Source: []proxyVirtualHostMetadataSource{
|
||||
{
|
||||
Kind: "*v1.VirtualService",
|
||||
Name: "my-internal-svc",
|
||||
Namespace: "internal",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var internalProxySvc = corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: internalProxy.Metadata.Name,
|
||||
Namespace: internalProxy.Metadata.Namespace,
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Type: corev1.ServiceTypeLoadBalancer,
|
||||
},
|
||||
Status: corev1.ServiceStatus{
|
||||
LoadBalancer: corev1.LoadBalancerStatus{
|
||||
Ingress: []corev1.LoadBalancerIngress{
|
||||
corev1.LoadBalancerIngress{
|
||||
IP: "203.0.113.1",
|
||||
},
|
||||
corev1.LoadBalancerIngress{
|
||||
IP: "203.0.113.2",
|
||||
},
|
||||
corev1.LoadBalancerIngress{
|
||||
IP: "203.0.113.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var internalProxySource = metav1.PartialObjectMetadata{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: virtualServiceGVR.GroupVersion().String(),
|
||||
Kind: "VirtualService",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: internalProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Name,
|
||||
Namespace: internalProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Namespace,
|
||||
Annotations: map[string]string{
|
||||
"external-dns.alpha.kubernetes.io/ttl": "42",
|
||||
"external-dns.alpha.kubernetes.io/aws-geolocation-country-code": "LU",
|
||||
"external-dns.alpha.kubernetes.io/set-identifier": "identifier",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// External proxy test
|
||||
var externalProxy = proxy{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: proxyGVR.GroupVersion().String(),
|
||||
Kind: "Proxy",
|
||||
},
|
||||
Metadata: metav1.ObjectMeta{
|
||||
Name: "external",
|
||||
Namespace: defaultGlooNamespace,
|
||||
},
|
||||
Spec: proxySpec{
|
||||
Listeners: []proxySpecListener{
|
||||
{
|
||||
HTTPListener: proxySpecHTTPListener{
|
||||
VirtualHosts: []proxyVirtualHost{
|
||||
{
|
||||
Domains: []string{"d.test"},
|
||||
Metadata: proxyVirtualHostMetadata{
|
||||
Source: []proxyVirtualHostMetadataSource{
|
||||
{
|
||||
Kind: "*v1.Unknown",
|
||||
Name: "my-unknown-svc",
|
||||
Namespace: "unknown",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: []string{"e.test"},
|
||||
Metadata: proxyVirtualHostMetadata{
|
||||
Source: []proxyVirtualHostMetadataSource{
|
||||
{
|
||||
Kind: "*v1.VirtualService",
|
||||
Name: "my-external-svc",
|
||||
Namespace: "external",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var externalProxySvc = corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: externalProxy.Metadata.Name,
|
||||
Namespace: externalProxy.Metadata.Namespace,
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Type: corev1.ServiceTypeLoadBalancer,
|
||||
},
|
||||
Status: corev1.ServiceStatus{
|
||||
LoadBalancer: corev1.LoadBalancerStatus{
|
||||
Ingress: []corev1.LoadBalancerIngress{
|
||||
corev1.LoadBalancerIngress{
|
||||
Hostname: "a.example.org",
|
||||
},
|
||||
corev1.LoadBalancerIngress{
|
||||
Hostname: "b.example.org",
|
||||
},
|
||||
corev1.LoadBalancerIngress{
|
||||
Hostname: "c.example.org",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var externalProxySource = metav1.PartialObjectMetadata{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: virtualServiceGVR.GroupVersion().String(),
|
||||
Kind: "VirtualService",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: externalProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Name,
|
||||
Namespace: externalProxy.Spec.Listeners[0].HTTPListener.VirtualHosts[1].Metadata.Source[0].Namespace,
|
||||
Annotations: map[string]string{
|
||||
"external-dns.alpha.kubernetes.io/ttl": "24",
|
||||
"external-dns.alpha.kubernetes.io/aws-geolocation-country-code": "JP",
|
||||
"external-dns.alpha.kubernetes.io/set-identifier": "identifier-external",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestGlooSource(t *testing.T) {
|
||||
fakeKubernetesClient := fakeKube.NewSimpleClientset()
|
||||
fakeDynamicClient := fakeDynamic.NewSimpleDynamicClient(runtime.NewScheme())
|
||||
|
||||
source, err := NewGlooSource(fakeDynamicClient, fakeKubernetesClient, defaultGlooNamespace)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, source)
|
||||
|
||||
internalProxyUnstructured := unstructured.Unstructured{}
|
||||
externalProxyUnstructured := unstructured.Unstructured{}
|
||||
|
||||
internalProxySourceUnstructured := unstructured.Unstructured{}
|
||||
externalProxySourceUnstructured := unstructured.Unstructured{}
|
||||
|
||||
internalProxyAsJSON, err := json.Marshal(internalProxy)
|
||||
assert.NoError(t, err)
|
||||
|
||||
externalProxyAsJSON, err := json.Marshal(externalProxy)
|
||||
assert.NoError(t, err)
|
||||
|
||||
internalProxySvcAsJSON, err := json.Marshal(internalProxySource)
|
||||
assert.NoError(t, err)
|
||||
|
||||
externalProxySvcAsJSON, err := json.Marshal(externalProxySource)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, internalProxyUnstructured.UnmarshalJSON(internalProxyAsJSON))
|
||||
assert.NoError(t, externalProxyUnstructured.UnmarshalJSON(externalProxyAsJSON))
|
||||
|
||||
assert.NoError(t, internalProxySourceUnstructured.UnmarshalJSON(internalProxySvcAsJSON))
|
||||
assert.NoError(t, externalProxySourceUnstructured.UnmarshalJSON(externalProxySvcAsJSON))
|
||||
|
||||
// Create proxy resources
|
||||
_, err = fakeDynamicClient.Resource(proxyGVR).Namespace(defaultGlooNamespace).Create(context.Background(), &internalProxyUnstructured, metav1.CreateOptions{})
|
||||
assert.NoError(t, err)
|
||||
_, err = fakeDynamicClient.Resource(proxyGVR).Namespace(defaultGlooNamespace).Create(context.Background(), &externalProxyUnstructured, metav1.CreateOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create proxy source
|
||||
_, err = fakeDynamicClient.Resource(virtualServiceGVR).Namespace(internalProxySource.Namespace).Create(context.Background(), &internalProxySourceUnstructured, metav1.CreateOptions{})
|
||||
assert.NoError(t, err)
|
||||
_, err = fakeDynamicClient.Resource(virtualServiceGVR).Namespace(externalProxySource.Namespace).Create(context.Background(), &externalProxySourceUnstructured, metav1.CreateOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create proxy service resources
|
||||
_, err = fakeKubernetesClient.CoreV1().Services(internalProxySvc.GetNamespace()).Create(context.Background(), &internalProxySvc, metav1.CreateOptions{})
|
||||
assert.NoError(t, err)
|
||||
_, err = fakeKubernetesClient.CoreV1().Services(externalProxySvc.GetNamespace()).Create(context.Background(), &externalProxySvc, metav1.CreateOptions{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
endpoints, err := source.Endpoints(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, endpoints, 5)
|
||||
assert.Equal(t, endpoints, []*endpoint.Endpoint{
|
||||
&endpoint.Endpoint{
|
||||
DNSName: "a.test",
|
||||
Targets: []string{internalProxySvc.Status.LoadBalancer.Ingress[0].IP, internalProxySvc.Status.LoadBalancer.Ingress[1].IP, internalProxySvc.Status.LoadBalancer.Ingress[2].IP},
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
RecordTTL: 0,
|
||||
Labels: endpoint.Labels{},
|
||||
ProviderSpecific: endpoint.ProviderSpecific{},
|
||||
},
|
||||
&endpoint.Endpoint{
|
||||
DNSName: "b.test",
|
||||
Targets: []string{internalProxySvc.Status.LoadBalancer.Ingress[0].IP, internalProxySvc.Status.LoadBalancer.Ingress[1].IP, internalProxySvc.Status.LoadBalancer.Ingress[2].IP},
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
RecordTTL: 0,
|
||||
Labels: endpoint.Labels{},
|
||||
ProviderSpecific: endpoint.ProviderSpecific{},
|
||||
},
|
||||
&endpoint.Endpoint{
|
||||
DNSName: "c.test",
|
||||
Targets: []string{internalProxySvc.Status.LoadBalancer.Ingress[0].IP, internalProxySvc.Status.LoadBalancer.Ingress[1].IP, internalProxySvc.Status.LoadBalancer.Ingress[2].IP},
|
||||
RecordType: endpoint.RecordTypeA,
|
||||
SetIdentifier: "identifier",
|
||||
RecordTTL: 42,
|
||||
Labels: endpoint.Labels{},
|
||||
ProviderSpecific: endpoint.ProviderSpecific{
|
||||
endpoint.ProviderSpecificProperty{
|
||||
Name: "aws/geolocation-country-code",
|
||||
Value: "LU",
|
||||
},
|
||||
},
|
||||
},
|
||||
&endpoint.Endpoint{
|
||||
DNSName: "d.test",
|
||||
Targets: []string{externalProxySvc.Status.LoadBalancer.Ingress[0].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[1].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[2].Hostname},
|
||||
RecordType: endpoint.RecordTypeCNAME,
|
||||
RecordTTL: 0,
|
||||
Labels: endpoint.Labels{},
|
||||
ProviderSpecific: endpoint.ProviderSpecific{},
|
||||
},
|
||||
&endpoint.Endpoint{
|
||||
DNSName: "e.test",
|
||||
Targets: []string{externalProxySvc.Status.LoadBalancer.Ingress[0].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[1].Hostname, externalProxySvc.Status.LoadBalancer.Ingress[2].Hostname},
|
||||
RecordType: endpoint.RecordTypeCNAME,
|
||||
SetIdentifier: "identifier-external",
|
||||
RecordTTL: 24,
|
||||
Labels: endpoint.Labels{},
|
||||
ProviderSpecific: endpoint.ProviderSpecific{
|
||||
endpoint.ProviderSpecificProperty{
|
||||
Name: "aws/geolocation-country-code",
|
||||
Value: "JP",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
@ -61,6 +61,7 @@ type Config struct {
|
||||
CFUsername string
|
||||
CFPassword string
|
||||
ContourLoadBalancerService string
|
||||
GlooNamespace string
|
||||
SkipperRouteGroupVersion string
|
||||
RequestTimeout time.Duration
|
||||
}
|
||||
@ -239,6 +240,16 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
|
||||
return nil, err
|
||||
}
|
||||
return NewContourHTTPProxySource(dynamicClient, cfg.Namespace, cfg.AnnotationFilter, cfg.FQDNTemplate, cfg.CombineFQDNAndAnnotation, cfg.IgnoreHostnameAnnotation)
|
||||
case "gloo-proxy":
|
||||
kubernetesClient, err := p.KubeClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dynamicClient, err := p.DynamicKubernetesClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewGlooSource(dynamicClient, kubernetesClient, cfg.GlooNamespace)
|
||||
case "openshift-route":
|
||||
ocpClient, err := p.OpenShiftClient()
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user