Commit 096c68be authored by Martin Linkhorst's avatar Martin Linkhorst Committed by GitHub
Browse files

Enhance compatibility and process molecule Services (#166)

* feat(service): enhance compatibility, process molecule services

* ref(service): simplify label detection for molecule servics
parent 285cb4cb
Showing with 108 additions and 35 deletions
+108 -35
......@@ -40,7 +40,7 @@ type Config struct {
Provider string
GoogleProject string
Policy string
Compatibility bool
Compatibility string
MetricsAddress string
Interval time.Duration
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.GoogleProject, "google-project", "", "gcloud project to target")
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.LogFormat, "log-format", defaultLogFormat, "log format output: <text|json>")
flags.DurationVar(&cfg.Interval, "interval", time.Minute, "interval between synchronizations")
......
......@@ -42,7 +42,7 @@ func TestParseFlags(t *testing.T) {
Provider: "",
GoogleProject: "",
Policy: "sync",
Compatibility: false,
Compatibility: "",
MetricsAddress: defaultMetricsAddress,
Interval: time.Minute,
Once: false,
......@@ -69,7 +69,7 @@ func TestParseFlags(t *testing.T) {
Provider: "",
GoogleProject: "",
Policy: "sync",
Compatibility: false,
Compatibility: "",
MetricsAddress: defaultMetricsAddress,
Interval: time.Minute,
Once: false,
......@@ -96,7 +96,7 @@ func TestParseFlags(t *testing.T) {
Provider: "",
GoogleProject: "",
Policy: "sync",
Compatibility: false,
Compatibility: "",
MetricsAddress: defaultMetricsAddress,
Interval: time.Minute,
Once: false,
......@@ -128,7 +128,7 @@ func TestParseFlags(t *testing.T) {
Provider: "",
GoogleProject: "",
Policy: "sync",
Compatibility: false,
Compatibility: "",
MetricsAddress: defaultMetricsAddress,
Interval: time.Minute,
Once: false,
......@@ -154,7 +154,7 @@ func TestParseFlags(t *testing.T) {
"--provider", "provider",
"--google-project", "project",
"--policy", "upsert-only",
"--compatibility",
"--compatibility=mate",
"--metrics-address", "127.0.0.1:9099",
"--interval", "10m",
"--once",
......@@ -175,7 +175,7 @@ func TestParseFlags(t *testing.T) {
Provider: "provider",
GoogleProject: "project",
Policy: "upsert-only",
Compatibility: true,
Compatibility: "mate",
MetricsAddress: "127.0.0.1:9099",
Interval: 10 * time.Minute,
Once: true,
......
......@@ -23,13 +23,21 @@ import (
)
const (
mateAnnotationKey = "zalando.org/dnsname"
mateAnnotationKey = "zalando.org/dnsname"
moleculeAnnotationKey = "domainName"
)
// legacyEndpointsFromService tries to retrieve Endpoints from Services
// annotated with legacy annotations.
func legacyEndpointsFromService(svc *v1.Service) []*endpoint.Endpoint {
return legacyEndpointsFromMateService(svc)
func legacyEndpointsFromService(svc *v1.Service, compatibility string) []*endpoint.Endpoint {
switch compatibility {
case "mate":
return legacyEndpointsFromMateService(svc)
case "molecule":
return legacyEndpointsFromMoleculeService(svc)
}
return []*endpoint.Endpoint{}
}
// legacyEndpointsFromMateService tries to retrieve Endpoints from Services
......@@ -55,3 +63,32 @@ func legacyEndpointsFromMateService(svc *v1.Service) []*endpoint.Endpoint {
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 {
client kubernetes.Interface
namespace string
// set to true to process Services with legacy annotations
compatibility bool
// process Services with legacy annotations
compatibility string
fqdntemplate *template.Template
}
// 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 err error
if fqdntemplate != "" {
......@@ -81,8 +81,8 @@ func (sc *serviceSource) Endpoints() ([]*endpoint.Endpoint, error) {
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 && sc.compatibility != "" {
svcEndpoints = legacyEndpointsFromService(&svc, sc.compatibility)
}
// 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) {
_, err := NewServiceSource(fake.NewSimpleClientset(), "", ti.fqdntemplate, false)
_, err := NewServiceSource(fake.NewSimpleClientset(), "", ti.fqdntemplate, "")
if ti.expectError && err == nil {
t.Error("invalid template should return err")
}
......@@ -73,8 +73,9 @@ func testServiceEndpoints(t *testing.T) {
targetNamespace string
svcNamespace string
svcName string
compatibility bool
compatibility string
fqdntemplate string
labels map[string]string
annotations map[string]string
lbs []string
expected []*endpoint.Endpoint
......@@ -84,8 +85,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{},
[]string{"1.2.3.4"},
[]*endpoint.Endpoint{},
......@@ -95,8 +97,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
hostnameAnnotationKey: "foo.example.org.",
},
......@@ -110,8 +113,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
hostnameAnnotationKey: "foo.example.org.",
},
......@@ -125,8 +129,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
hostnameAnnotationKey: "foo.example.org", // Trailing dot is omitted
},
......@@ -141,8 +146,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
controllerAnnotationKey: controllerAnnotationValue,
hostnameAnnotationKey: "foo.example.org.",
......@@ -157,8 +163,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"{{.Name}}.ext-dns.test.com",
map[string]string{},
map[string]string{
controllerAnnotationKey: "some-other-tool",
hostnameAnnotationKey: "foo.example.org.",
......@@ -171,8 +178,9 @@ func testServiceEndpoints(t *testing.T) {
"testing",
"testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
hostnameAnnotationKey: "foo.example.org.",
},
......@@ -186,8 +194,9 @@ func testServiceEndpoints(t *testing.T) {
"testing",
"other-testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
hostnameAnnotationKey: "foo.example.org.",
},
......@@ -199,8 +208,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"other-testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
hostnameAnnotationKey: "foo.example.org.",
},
......@@ -214,8 +224,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
hostnameAnnotationKey: "foo.example.org.",
},
......@@ -227,8 +238,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
hostnameAnnotationKey: "foo.example.org.",
},
......@@ -243,8 +255,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"",
map[string]string{},
map[string]string{
"zalando.org/dnsname": "foo.example.org.",
},
......@@ -256,8 +269,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
true,
"mate",
"",
map[string]string{},
map[string]string{
"zalando.org/dnsname": "foo.example.org.",
},
......@@ -266,14 +280,33 @@ func testServiceEndpoints(t *testing.T) {
{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",
"",
"testing",
"foo",
false,
"",
"{{.Name}}.bar.example.com",
map[string]string{},
map[string]string{},
[]string{"1.2.3.4", "elb.com"},
[]*endpoint.Endpoint{
{DNSName: "foo.bar.example.com", Target: "1.2.3.4"},
......@@ -285,9 +318,10 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
false,
"",
"{{.Calibre}}.bar.example.com",
map[string]string{},
map[string]string{},
[]string{"1.2.3.4"},
[]*endpoint.Endpoint{},
},
......@@ -296,8 +330,9 @@ func testServiceEndpoints(t *testing.T) {
"",
"testing",
"foo",
true,
"mate",
"{{.Name}}.bar.example.com",
map[string]string{},
map[string]string{
"zalando.org/dnsname": "mate.example.org.",
},
......@@ -325,6 +360,7 @@ func testServiceEndpoints(t *testing.T) {
ObjectMeta: v1.ObjectMeta{
Namespace: tc.svcNamespace,
Name: tc.svcName,
Labels: tc.labels,
Annotations: tc.annotations,
},
Status: v1.ServiceStatus{
......@@ -379,7 +415,7 @@ func BenchmarkServiceEndpoints(b *testing.B) {
b.Fatal(err)
}
client, _ := NewServiceSource(kubernetes, v1.NamespaceAll, "", false)
client, _ := NewServiceSource(kubernetes, v1.NamespaceAll, "", "")
for i := 0; i < b.N; i++ {
_, err := client.Endpoints()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment