mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-07 01:56:57 +02:00
Enhance compatibility and process molecule Services (#166)
* feat(service): enhance compatibility, process molecule services * ref(service): simplify label detection for molecule servics
This commit is contained in:
parent
285cb4cb35
commit
096c68be79
@ -40,7 +40,7 @@ type Config struct {
|
|||||||
Provider string
|
Provider string
|
||||||
GoogleProject string
|
GoogleProject string
|
||||||
Policy string
|
Policy string
|
||||||
Compatibility bool
|
Compatibility string
|
||||||
MetricsAddress string
|
MetricsAddress string
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
Once bool
|
Once bool
|
||||||
@ -71,7 +71,7 @@ func (cfg *Config) ParseFlags(args []string) error {
|
|||||||
flags.StringVar(&cfg.Provider, "provider", "", "the DNS provider to materialize the records in: <aws|google>")
|
flags.StringVar(&cfg.Provider, "provider", "", "the DNS provider to materialize the records in: <aws|google>")
|
||||||
flags.StringVar(&cfg.GoogleProject, "google-project", "", "gcloud project to target")
|
flags.StringVar(&cfg.GoogleProject, "google-project", "", "gcloud project to target")
|
||||||
flags.StringVar(&cfg.Policy, "policy", "sync", "the policy to use: <sync|upsert-only>")
|
flags.StringVar(&cfg.Policy, "policy", "sync", "the policy to use: <sync|upsert-only>")
|
||||||
flags.BoolVar(&cfg.Compatibility, "compatibility", false, "enable to process annotation semantics from legacy implementations")
|
flags.StringVar(&cfg.Compatibility, "compatibility", "", "enable to process annotation semantics from legacy implementations: <mate|molecule>")
|
||||||
flags.StringVar(&cfg.MetricsAddress, "metrics-address", defaultMetricsAddress, "address to expose metrics on")
|
flags.StringVar(&cfg.MetricsAddress, "metrics-address", defaultMetricsAddress, "address to expose metrics on")
|
||||||
flags.StringVar(&cfg.LogFormat, "log-format", defaultLogFormat, "log format output: <text|json>")
|
flags.StringVar(&cfg.LogFormat, "log-format", defaultLogFormat, "log format output: <text|json>")
|
||||||
flags.DurationVar(&cfg.Interval, "interval", time.Minute, "interval between synchronizations")
|
flags.DurationVar(&cfg.Interval, "interval", time.Minute, "interval between synchronizations")
|
||||||
|
@ -42,7 +42,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
Provider: "",
|
Provider: "",
|
||||||
GoogleProject: "",
|
GoogleProject: "",
|
||||||
Policy: "sync",
|
Policy: "sync",
|
||||||
Compatibility: false,
|
Compatibility: "",
|
||||||
MetricsAddress: defaultMetricsAddress,
|
MetricsAddress: defaultMetricsAddress,
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
Once: false,
|
Once: false,
|
||||||
@ -69,7 +69,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
Provider: "",
|
Provider: "",
|
||||||
GoogleProject: "",
|
GoogleProject: "",
|
||||||
Policy: "sync",
|
Policy: "sync",
|
||||||
Compatibility: false,
|
Compatibility: "",
|
||||||
MetricsAddress: defaultMetricsAddress,
|
MetricsAddress: defaultMetricsAddress,
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
Once: false,
|
Once: false,
|
||||||
@ -96,7 +96,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
Provider: "",
|
Provider: "",
|
||||||
GoogleProject: "",
|
GoogleProject: "",
|
||||||
Policy: "sync",
|
Policy: "sync",
|
||||||
Compatibility: false,
|
Compatibility: "",
|
||||||
MetricsAddress: defaultMetricsAddress,
|
MetricsAddress: defaultMetricsAddress,
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
Once: false,
|
Once: false,
|
||||||
@ -128,7 +128,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
Provider: "",
|
Provider: "",
|
||||||
GoogleProject: "",
|
GoogleProject: "",
|
||||||
Policy: "sync",
|
Policy: "sync",
|
||||||
Compatibility: false,
|
Compatibility: "",
|
||||||
MetricsAddress: defaultMetricsAddress,
|
MetricsAddress: defaultMetricsAddress,
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
Once: false,
|
Once: false,
|
||||||
@ -154,7 +154,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"--provider", "provider",
|
"--provider", "provider",
|
||||||
"--google-project", "project",
|
"--google-project", "project",
|
||||||
"--policy", "upsert-only",
|
"--policy", "upsert-only",
|
||||||
"--compatibility",
|
"--compatibility=mate",
|
||||||
"--metrics-address", "127.0.0.1:9099",
|
"--metrics-address", "127.0.0.1:9099",
|
||||||
"--interval", "10m",
|
"--interval", "10m",
|
||||||
"--once",
|
"--once",
|
||||||
@ -175,7 +175,7 @@ func TestParseFlags(t *testing.T) {
|
|||||||
Provider: "provider",
|
Provider: "provider",
|
||||||
GoogleProject: "project",
|
GoogleProject: "project",
|
||||||
Policy: "upsert-only",
|
Policy: "upsert-only",
|
||||||
Compatibility: true,
|
Compatibility: "mate",
|
||||||
MetricsAddress: "127.0.0.1:9099",
|
MetricsAddress: "127.0.0.1:9099",
|
||||||
Interval: 10 * time.Minute,
|
Interval: 10 * time.Minute,
|
||||||
Once: true,
|
Once: true,
|
||||||
|
@ -24,12 +24,20 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
mateAnnotationKey = "zalando.org/dnsname"
|
mateAnnotationKey = "zalando.org/dnsname"
|
||||||
|
moleculeAnnotationKey = "domainName"
|
||||||
)
|
)
|
||||||
|
|
||||||
// legacyEndpointsFromService tries to retrieve Endpoints from Services
|
// legacyEndpointsFromService tries to retrieve Endpoints from Services
|
||||||
// annotated with legacy annotations.
|
// annotated with legacy annotations.
|
||||||
func legacyEndpointsFromService(svc *v1.Service) []*endpoint.Endpoint {
|
func legacyEndpointsFromService(svc *v1.Service, compatibility string) []*endpoint.Endpoint {
|
||||||
|
switch compatibility {
|
||||||
|
case "mate":
|
||||||
return legacyEndpointsFromMateService(svc)
|
return legacyEndpointsFromMateService(svc)
|
||||||
|
case "molecule":
|
||||||
|
return legacyEndpointsFromMoleculeService(svc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []*endpoint.Endpoint{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// legacyEndpointsFromMateService tries to retrieve Endpoints from Services
|
// legacyEndpointsFromMateService tries to retrieve Endpoints from Services
|
||||||
@ -55,3 +63,32 @@ func legacyEndpointsFromMateService(svc *v1.Service) []*endpoint.Endpoint {
|
|||||||
|
|
||||||
return endpoints
|
return endpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// legacyEndpointsFromMoleculeService tries to retrieve Endpoints from Services
|
||||||
|
// annotated with Molecule Software's annotation semantics.
|
||||||
|
func legacyEndpointsFromMoleculeService(svc *v1.Service) []*endpoint.Endpoint {
|
||||||
|
var endpoints []*endpoint.Endpoint
|
||||||
|
|
||||||
|
// Check that the Service opted-in to being processed.
|
||||||
|
if svc.Labels["dns"] != "route53" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the desired hostname of the service from the annotation.
|
||||||
|
hostname, exists := svc.Annotations[moleculeAnnotationKey]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
@ -36,13 +36,13 @@ import (
|
|||||||
type serviceSource struct {
|
type serviceSource struct {
|
||||||
client kubernetes.Interface
|
client kubernetes.Interface
|
||||||
namespace string
|
namespace string
|
||||||
// set to true to process Services with legacy annotations
|
// process Services with legacy annotations
|
||||||
compatibility bool
|
compatibility string
|
||||||
fqdntemplate *template.Template
|
fqdntemplate *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServiceSource creates a new serviceSource with the given client and namespace scope.
|
// NewServiceSource creates a new serviceSource with the given client and namespace scope.
|
||||||
func NewServiceSource(client kubernetes.Interface, namespace, fqdntemplate string, compatibility bool) (Source, error) {
|
func NewServiceSource(client kubernetes.Interface, namespace, fqdntemplate string, compatibility string) (Source, error) {
|
||||||
var tmpl *template.Template
|
var tmpl *template.Template
|
||||||
var err error
|
var err error
|
||||||
if fqdntemplate != "" {
|
if fqdntemplate != "" {
|
||||||
@ -81,8 +81,8 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) {
|
|||||||
svcEndpoints := endpointsFromService(&svc)
|
svcEndpoints := endpointsFromService(&svc)
|
||||||
|
|
||||||
// process legacy annotations if no endpoints were returned and compatibility mode is enabled.
|
// process legacy annotations if no endpoints were returned and compatibility mode is enabled.
|
||||||
if len(svcEndpoints) == 0 && sc.compatibility {
|
if len(svcEndpoints) == 0 && sc.compatibility != "" {
|
||||||
svcEndpoints = legacyEndpointsFromService(&svc)
|
svcEndpoints = legacyEndpointsFromService(&svc, sc.compatibility)
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply template if none of the above is found
|
// apply template if none of the above is found
|
||||||
|
@ -55,7 +55,7 @@ func TestNewServiceSource(t *testing.T) {
|
|||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(ti.title, func(t *testing.T) {
|
t.Run(ti.title, func(t *testing.T) {
|
||||||
_, err := NewServiceSource(fake.NewSimpleClientset(), "", ti.fqdntemplate, false)
|
_, err := NewServiceSource(fake.NewSimpleClientset(), "", ti.fqdntemplate, "")
|
||||||
if ti.expectError && err == nil {
|
if ti.expectError && err == nil {
|
||||||
t.Error("invalid template should return err")
|
t.Error("invalid template should return err")
|
||||||
}
|
}
|
||||||
@ -73,8 +73,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
targetNamespace string
|
targetNamespace string
|
||||||
svcNamespace string
|
svcNamespace string
|
||||||
svcName string
|
svcName string
|
||||||
compatibility bool
|
compatibility string
|
||||||
fqdntemplate string
|
fqdntemplate string
|
||||||
|
labels map[string]string
|
||||||
annotations map[string]string
|
annotations map[string]string
|
||||||
lbs []string
|
lbs []string
|
||||||
expected []*endpoint.Endpoint
|
expected []*endpoint.Endpoint
|
||||||
@ -84,8 +85,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
[]string{"1.2.3.4"},
|
[]string{"1.2.3.4"},
|
||||||
[]*endpoint.Endpoint{},
|
[]*endpoint.Endpoint{},
|
||||||
@ -95,8 +97,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
hostnameAnnotationKey: "foo.example.org.",
|
hostnameAnnotationKey: "foo.example.org.",
|
||||||
},
|
},
|
||||||
@ -110,8 +113,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
hostnameAnnotationKey: "foo.example.org.",
|
hostnameAnnotationKey: "foo.example.org.",
|
||||||
},
|
},
|
||||||
@ -125,8 +129,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted
|
hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted
|
||||||
},
|
},
|
||||||
@ -141,8 +146,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
controllerAnnotationKey: controllerAnnotationValue,
|
controllerAnnotationKey: controllerAnnotationValue,
|
||||||
hostnameAnnotationKey: "foo.example.org.",
|
hostnameAnnotationKey: "foo.example.org.",
|
||||||
@ -157,8 +163,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
"",
|
||||||
"{{.Name}}.ext-dns.test.com",
|
"{{.Name}}.ext-dns.test.com",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
controllerAnnotationKey: "some-other-tool",
|
controllerAnnotationKey: "some-other-tool",
|
||||||
hostnameAnnotationKey: "foo.example.org.",
|
hostnameAnnotationKey: "foo.example.org.",
|
||||||
@ -171,8 +178,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"testing",
|
"testing",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
hostnameAnnotationKey: "foo.example.org.",
|
hostnameAnnotationKey: "foo.example.org.",
|
||||||
},
|
},
|
||||||
@ -186,8 +194,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"testing",
|
"testing",
|
||||||
"other-testing",
|
"other-testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
hostnameAnnotationKey: "foo.example.org.",
|
hostnameAnnotationKey: "foo.example.org.",
|
||||||
},
|
},
|
||||||
@ -199,8 +208,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"other-testing",
|
"other-testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
hostnameAnnotationKey: "foo.example.org.",
|
hostnameAnnotationKey: "foo.example.org.",
|
||||||
},
|
},
|
||||||
@ -214,8 +224,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
hostnameAnnotationKey: "foo.example.org.",
|
hostnameAnnotationKey: "foo.example.org.",
|
||||||
},
|
},
|
||||||
@ -227,8 +238,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
hostnameAnnotationKey: "foo.example.org.",
|
hostnameAnnotationKey: "foo.example.org.",
|
||||||
},
|
},
|
||||||
@ -243,8 +255,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"zalando.org/dnsname": "foo.example.org.",
|
"zalando.org/dnsname": "foo.example.org.",
|
||||||
},
|
},
|
||||||
@ -256,8 +269,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
"mate",
|
||||||
"",
|
"",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"zalando.org/dnsname": "foo.example.org.",
|
"zalando.org/dnsname": "foo.example.org.",
|
||||||
},
|
},
|
||||||
@ -266,14 +280,33 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
{DNSName: "foo.example.org", Target: "1.2.3.4"},
|
{DNSName: "foo.example.org", Target: "1.2.3.4"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"services annotated with legacy molecule annotations return an endpoint in compatibility mode",
|
||||||
|
"",
|
||||||
|
"testing",
|
||||||
|
"foo",
|
||||||
|
"molecule",
|
||||||
|
"",
|
||||||
|
map[string]string{
|
||||||
|
"dns": "route53",
|
||||||
|
},
|
||||||
|
map[string]string{
|
||||||
|
"domainName": "foo.example.org.",
|
||||||
|
},
|
||||||
|
[]string{"1.2.3.4"},
|
||||||
|
[]*endpoint.Endpoint{
|
||||||
|
{DNSName: "foo.example.org", Target: "1.2.3.4"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"not annotated services with set fqdntemplate return an endpoint with target IP",
|
"not annotated services with set fqdntemplate return an endpoint with target IP",
|
||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
"",
|
||||||
"{{.Name}}.bar.example.com",
|
"{{.Name}}.bar.example.com",
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
|
map[string]string{},
|
||||||
[]string{"1.2.3.4", "elb.com"},
|
[]string{"1.2.3.4", "elb.com"},
|
||||||
[]*endpoint.Endpoint{
|
[]*endpoint.Endpoint{
|
||||||
{DNSName: "foo.bar.example.com", Target: "1.2.3.4"},
|
{DNSName: "foo.bar.example.com", Target: "1.2.3.4"},
|
||||||
@ -285,9 +318,10 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
"",
|
||||||
"{{.Calibre}}.bar.example.com",
|
"{{.Calibre}}.bar.example.com",
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
|
map[string]string{},
|
||||||
[]string{"1.2.3.4"},
|
[]string{"1.2.3.4"},
|
||||||
[]*endpoint.Endpoint{},
|
[]*endpoint.Endpoint{},
|
||||||
},
|
},
|
||||||
@ -296,8 +330,9 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
"",
|
"",
|
||||||
"testing",
|
"testing",
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
"mate",
|
||||||
"{{.Name}}.bar.example.com",
|
"{{.Name}}.bar.example.com",
|
||||||
|
map[string]string{},
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"zalando.org/dnsname": "mate.example.org.",
|
"zalando.org/dnsname": "mate.example.org.",
|
||||||
},
|
},
|
||||||
@ -325,6 +360,7 @@ func testServiceEndpoints(t *testing.T) {
|
|||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
Namespace: tc.svcNamespace,
|
Namespace: tc.svcNamespace,
|
||||||
Name: tc.svcName,
|
Name: tc.svcName,
|
||||||
|
Labels: tc.labels,
|
||||||
Annotations: tc.annotations,
|
Annotations: tc.annotations,
|
||||||
},
|
},
|
||||||
Status: v1.ServiceStatus{
|
Status: v1.ServiceStatus{
|
||||||
@ -379,7 +415,7 @@ func BenchmarkServiceEndpoints(b *testing.B) {
|
|||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
client, _ := NewServiceSource(kubernetes, v1.NamespaceAll, "", false)
|
client, _ := NewServiceSource(kubernetes, v1.NamespaceAll, "", "")
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := client.Endpoints()
|
_, err := client.Endpoints()
|
||||||
|
Loading…
Reference in New Issue
Block a user