mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 01:26:59 +02:00
feat(source): support services annotated with mate's annotations (#141)
This commit is contained in:
parent
04bbdb5d80
commit
9d48d89240
2
main.go
2
main.go
@ -76,7 +76,7 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
source.Register("service", source.NewServiceSource(client, cfg.Namespace))
|
||||
source.Register("service", source.NewServiceSource(client, cfg.Namespace, cfg.Compatibility))
|
||||
source.Register("ingress", source.NewIngressSource(client, cfg.Namespace))
|
||||
|
||||
sources := source.NewMultiSource(source.LookupMultiple(cfg.Sources...)...)
|
||||
|
@ -38,6 +38,7 @@ type Config struct {
|
||||
Sources []string
|
||||
Provider string
|
||||
GoogleProject string
|
||||
Compatibility bool
|
||||
MetricsAddress string
|
||||
Interval time.Duration
|
||||
Once bool
|
||||
@ -62,6 +63,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
flags.StringArrayVar(&cfg.Sources, "source", nil, "the sources to gather endpoints from")
|
||||
flags.StringVar(&cfg.Provider, "provider", "", "the DNS provider to materialize the records in")
|
||||
flags.StringVar(&cfg.GoogleProject, "google-project", "", "gcloud project to target")
|
||||
flags.BoolVar(&cfg.Compatibility, "compatibility", false, "enable to process annotation semantics from legacy implementations")
|
||||
flags.StringVar(&cfg.MetricsAddress, "metrics-address", defaultMetricsAddress, "address to expose metrics on")
|
||||
flags.StringVar(&cfg.LogFormat, "log-format", defaultLogFormat, "log format output. options: [\"text\", \"json\"]")
|
||||
flags.DurationVar(&cfg.Interval, "interval", time.Minute, "interval between synchronizations")
|
||||
|
@ -40,6 +40,7 @@ func TestParseFlags(t *testing.T) {
|
||||
Sources: nil,
|
||||
Provider: "",
|
||||
GoogleProject: "",
|
||||
Compatibility: false,
|
||||
MetricsAddress: defaultMetricsAddress,
|
||||
Interval: time.Minute,
|
||||
Once: false,
|
||||
@ -60,6 +61,7 @@ func TestParseFlags(t *testing.T) {
|
||||
Sources: nil,
|
||||
Provider: "",
|
||||
GoogleProject: "",
|
||||
Compatibility: false,
|
||||
MetricsAddress: defaultMetricsAddress,
|
||||
Interval: time.Minute,
|
||||
Once: false,
|
||||
@ -80,6 +82,7 @@ func TestParseFlags(t *testing.T) {
|
||||
Sources: nil,
|
||||
Provider: "",
|
||||
GoogleProject: "",
|
||||
Compatibility: false,
|
||||
MetricsAddress: defaultMetricsAddress,
|
||||
Interval: time.Minute,
|
||||
Once: false,
|
||||
@ -105,6 +108,7 @@ func TestParseFlags(t *testing.T) {
|
||||
Sources: nil,
|
||||
Provider: "",
|
||||
GoogleProject: "",
|
||||
Compatibility: false,
|
||||
MetricsAddress: defaultMetricsAddress,
|
||||
Interval: time.Minute,
|
||||
Once: false,
|
||||
@ -124,6 +128,7 @@ func TestParseFlags(t *testing.T) {
|
||||
"--source", "source",
|
||||
"--provider", "provider",
|
||||
"--google-project", "project",
|
||||
"--compatibility",
|
||||
"--metrics-address", "127.0.0.1:9099",
|
||||
"--interval", "10m",
|
||||
"--once",
|
||||
@ -138,6 +143,7 @@ func TestParseFlags(t *testing.T) {
|
||||
Sources: []string{"source"},
|
||||
Provider: "provider",
|
||||
GoogleProject: "project",
|
||||
Compatibility: true,
|
||||
MetricsAddress: "127.0.0.1:9099",
|
||||
Interval: 10 * time.Minute,
|
||||
Once: true,
|
||||
|
57
source/compatibility.go
Normal file
57
source/compatibility.go
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2017 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 (
|
||||
"k8s.io/client-go/pkg/api/v1"
|
||||
|
||||
"github.com/kubernetes-incubator/external-dns/endpoint"
|
||||
)
|
||||
|
||||
const (
|
||||
mateAnnotationKey = "zalando.org/dnsname"
|
||||
)
|
||||
|
||||
// legacyEndpointsFromService tries to retrieve Endpoints from Services
|
||||
// annotated with legacy annotations.
|
||||
func legacyEndpointsFromService(svc *v1.Service) []*endpoint.Endpoint {
|
||||
return legacyEndpointsFromMateService(svc)
|
||||
}
|
||||
|
||||
// legacyEndpointsFromMateService tries to retrieve Endpoints from Services
|
||||
// annotated with Mate's annotation semantics.
|
||||
func legacyEndpointsFromMateService(svc *v1.Service) []*endpoint.Endpoint {
|
||||
var endpoints []*endpoint.Endpoint
|
||||
|
||||
// Get the desired hostname of the service from the annotation.
|
||||
hostname, exists := svc.Annotations[mateAnnotationKey]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a corresponding endpoint for each configured external entrypoint.
|
||||
for _, lb := range svc.Status.LoadBalancer.Ingress {
|
||||
if lb.IP != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.IP))
|
||||
}
|
||||
if lb.Hostname != "" {
|
||||
endpoints = append(endpoints, endpoint.NewEndpoint(hostname, lb.Hostname))
|
||||
}
|
||||
}
|
||||
|
||||
return endpoints
|
||||
}
|
@ -31,11 +31,17 @@ import (
|
||||
type serviceSource struct {
|
||||
client kubernetes.Interface
|
||||
namespace string
|
||||
// set to true to process Services with legacy annotations
|
||||
compatibility bool
|
||||
}
|
||||
|
||||
// NewServiceSource creates a new serviceSource with the given client and namespace scope.
|
||||
func NewServiceSource(client kubernetes.Interface, namespace string) Source {
|
||||
return &serviceSource{client: client, namespace: namespace}
|
||||
func NewServiceSource(client kubernetes.Interface, namespace string, compatibility bool) Source {
|
||||
return &serviceSource{
|
||||
client: client,
|
||||
namespace: namespace,
|
||||
compatibility: compatibility,
|
||||
}
|
||||
}
|
||||
|
||||
// Endpoints returns endpoint objects for each service that should be processed.
|
||||
@ -49,6 +55,12 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) {
|
||||
|
||||
for _, svc := range services.Items {
|
||||
svcEndpoints := endpointsFromService(&svc)
|
||||
|
||||
// process legacy annotations if no endpoints were returned and compatibility mode is enabled.
|
||||
if len(svcEndpoints) == 0 && sc.compatibility {
|
||||
svcEndpoints = legacyEndpointsFromService(&svc)
|
||||
}
|
||||
|
||||
if len(svcEndpoints) != 0 {
|
||||
endpoints = append(endpoints, svcEndpoints...)
|
||||
}
|
||||
@ -64,13 +76,13 @@ func endpointsFromService(svc *v1.Service) []*endpoint.Endpoint {
|
||||
// Check controller annotation to see if we are responsible.
|
||||
controller, exists := svc.Annotations[controllerAnnotationKey]
|
||||
if exists && controller != controllerAnnotationValue {
|
||||
return endpoints
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the desired hostname of the service from the annotation.
|
||||
hostname, exists := svc.Annotations[hostnameAnnotationKey]
|
||||
if !exists {
|
||||
return endpoints
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a corresponding endpoint for each configured external entrypoint.
|
||||
|
@ -40,6 +40,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
targetNamespace string
|
||||
svcNamespace string
|
||||
svcName string
|
||||
compatibility bool
|
||||
annotations map[string]string
|
||||
lbs []string
|
||||
expected []*endpoint.Endpoint
|
||||
@ -49,6 +50,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{},
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{},
|
||||
@ -58,6 +60,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
@ -71,6 +74,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
@ -84,6 +88,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted
|
||||
},
|
||||
@ -98,6 +103,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
controllerAnnotationKey: controllerAnnotationValue,
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
@ -112,6 +118,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
controllerAnnotationKey: "some-other-tool",
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
@ -124,6 +131,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"testing",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
@ -137,6 +145,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"testing",
|
||||
"other-testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
@ -148,6 +157,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"",
|
||||
"other-testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
@ -161,6 +171,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
@ -172,6 +183,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
hostnameAnnotationKey: "foo.example.org.",
|
||||
},
|
||||
@ -181,6 +193,32 @@ func testServiceEndpoints(t *testing.T) {
|
||||
{DNSName: "foo.example.org", Target: "8.8.8.8"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"services annotated with legacy mate annotations are ignored in default mode",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
false,
|
||||
map[string]string{
|
||||
"zalando.org/dnsname": "foo.example.org.",
|
||||
},
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{},
|
||||
},
|
||||
{
|
||||
"services annotated with legacy mate annotations return an endpoint in compatibility mode",
|
||||
"",
|
||||
"testing",
|
||||
"foo",
|
||||
true,
|
||||
map[string]string{
|
||||
"zalando.org/dnsname": "foo.example.org.",
|
||||
},
|
||||
[]string{"1.2.3.4"},
|
||||
[]*endpoint.Endpoint{
|
||||
{DNSName: "foo.example.org", Target: "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.title, func(t *testing.T) {
|
||||
// Create a Kubernetes testing client
|
||||
@ -215,7 +253,7 @@ func testServiceEndpoints(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create our object under test and get the endpoints.
|
||||
client := NewServiceSource(kubernetes, tc.targetNamespace)
|
||||
client := NewServiceSource(kubernetes, tc.targetNamespace, tc.compatibility)
|
||||
|
||||
endpoints, err := client.Endpoints()
|
||||
if err != nil {
|
||||
@ -254,7 +292,7 @@ func BenchmarkServiceEndpoints(b *testing.B) {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
client := NewServiceSource(kubernetes, v1.NamespaceAll)
|
||||
client := NewServiceSource(kubernetes, v1.NamespaceAll, false)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := client.Endpoints()
|
||||
|
Loading…
Reference in New Issue
Block a user