Commit 16194ca9 authored by Reinier Schoof's avatar Reinier Schoof
Browse files

implemented annotationFilter for node source

parent a491d8f6
Showing with 130 additions and 32 deletions
+130 -32
......@@ -25,6 +25,7 @@ import (
log "github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1"
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"
......@@ -36,12 +37,13 @@ import (
)
type nodeSource struct {
client kubernetes.Interface
fqdnTemplate *template.Template
nodeInformer coreinformers.NodeInformer
client kubernetes.Interface
annotationFilter string
fqdnTemplate *template.Template
nodeInformer coreinformers.NodeInformer
}
func NewNodeSource(kubeClient kubernetes.Interface, fqdnTemplate string) (Source, error) {
func NewNodeSource(kubeClient kubernetes.Interface, annotationFilter, fqdnTemplate string) (Source, error) {
var (
tmpl *template.Template
err error
......@@ -82,9 +84,10 @@ func NewNodeSource(kubeClient kubernetes.Interface, fqdnTemplate string) (Source
}
return &nodeSource{
client: kubeClient,
fqdnTemplate: tmpl,
nodeInformer: nodeInformer,
client: kubeClient,
annotationFilter: annotationFilter,
fqdnTemplate: tmpl,
nodeInformer: nodeInformer,
}, nil
}
......@@ -95,6 +98,11 @@ func (ns *nodeSource) Endpoints() ([]*endpoint.Endpoint, error) {
return nil, err
}
nodes, err = ns.filterByAnnotations(nodes)
if err != nil {
return nil, err
}
endpoints := []*endpoint.Endpoint{}
// create endpoints for all nodes
......@@ -149,3 +157,34 @@ func (ns *nodeSource) nodeAddress(node *v1.Node) (string, error) {
return "", fmt.Errorf("could not find node address for %s", node.Name)
}
// filterByAnnotations filters a list of nodes by a given annotation selector.
func (ns *nodeSource) filterByAnnotations(nodes []*v1.Node) ([]*v1.Node, error) {
labelSelector, err := metav1.ParseToLabelSelector(ns.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 nodes, nil
}
filteredList := []*v1.Node{}
for _, node := range nodes {
// convert the node's annotations to an equivalent label selector
annotations := labels.Set(node.Annotations)
// include node if its annotations match the selector
if selector.Matches(annotations) {
filteredList = append(filteredList, node)
}
}
return filteredList, nil
}
......@@ -14,16 +14,17 @@ import (
func TestNodeSource(t *testing.T) {
//suite.Run(t, new(ServiceSuite))
//t.Run("Interface", testServiceSourceImplementsSource)
//t.Run("NewNodeSource", testNodeSourceNewNodeSource)
t.Run("NewNodeSource", testNodeSourceNewNodeSource)
t.Run("Endpoints", testNodeSourceEndpoints)
}
// testNodeSourceNewNodeSource tests that NewNodeService doesn't return an error.
func testNodeSourceNewNodeSource(t *testing.T) {
for _, ti := range []struct {
title string
fqdnTemplate string
expectError bool
title string
annotationFilter string
fqdnTemplate string
expectError bool
}{
{
title: "invalid template",
......@@ -39,10 +40,16 @@ func testNodeSourceNewNodeSource(t *testing.T) {
expectError: false,
fqdnTemplate: "{{.Name}}-{{.Namespace}}.ext-dns.test.com",
},
{
title: "non-empty annotation filter label",
expectError: false,
annotationFilter: "kubernetes.io/ingress.class=nginx",
},
} {
t.Run(ti.title, func(t *testing.T) {
_, err := NewNodeSource(
fake.NewSimpleClientset(),
ti.annotationFilter,
ti.fqdnTemplate,
)
......@@ -58,66 +65,72 @@ func testNodeSourceNewNodeSource(t *testing.T) {
// testNodeSourceEndpoints tests that various node generate the correct endpoints.
func testNodeSourceEndpoints(t *testing.T) {
for _, tc := range []struct {
title string
fqdnTemplate string
nodeName string
nodeAddresses []v1.NodeAddress
labels map[string]string
annotations map[string]string
expected []*endpoint.Endpoint
expectError bool
title string
annotationFilter string
fqdnTemplate string
nodeName string
nodeAddresses []v1.NodeAddress
labels map[string]string
annotations map[string]string
expected []*endpoint.Endpoint
expectError bool
}{
{
"node with short hostname returns one endpoint",
"",
"",
"node1",
[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
map[string]string{},
map[string]string{},
[]*endpoint.Endpoint{
{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}},
},
{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}},
},
false,
},
{
{
"node with fqdn returns one endpoint",
"",
"",
"node1.example.org",
[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
map[string]string{},
map[string]string{},
[]*endpoint.Endpoint{
{RecordType: "A", DNSName: "node1.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
},
{RecordType: "A", DNSName: "node1.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
},
false,
},
{
"node with fqdn template returns endpoint with expanded hostname",
"",
"{{.Name}}.example.org",
"node1",
[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
map[string]string{},
map[string]string{},
[]*endpoint.Endpoint{
{RecordType: "A", DNSName: "node1.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
},
{RecordType: "A", DNSName: "node1.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
},
false,
},
{
{
"node with fqdn and fqdn template returns one endpoint",
"",
"{{.Name}}.example.org",
"node1.example.org",
[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
map[string]string{},
map[string]string{},
[]*endpoint.Endpoint{
{RecordType: "A", DNSName: "node1.example.org.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
},
{RecordType: "A", DNSName: "node1.example.org.example.org", Targets: endpoint.Targets{"1.2.3.4"}},
},
false,
},
{
"node with both external and internal IP returns an endpoint with external IP",
"",
"",
"node1",
[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}, {v1.NodeInternalIP, "2.3.4.5"}},
map[string]string{},
......@@ -127,9 +140,10 @@ func testNodeSourceEndpoints(t *testing.T) {
},
false,
},
{
{
"node with only internal IP returns an endpoint with internal IP",
"",
"",
"node1",
[]v1.NodeAddress{{v1.NodeInternalIP, "2.3.4.5"}},
map[string]string{},
......@@ -139,9 +153,10 @@ func testNodeSourceEndpoints(t *testing.T) {
},
false,
},
{
{
"node with neither external nor internal IP returns no endpoints",
"",
"",
"node1",
[]v1.NodeAddress{},
map[string]string{},
......@@ -149,6 +164,49 @@ func testNodeSourceEndpoints(t *testing.T) {
[]*endpoint.Endpoint{},
false,
},
{
"annotated node without annotation filter returns endpoint",
"",
"",
"node1",
[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
map[string]string{},
map[string]string{
"service.beta.kubernetes.io/external-traffic": "OnlyLocal",
},
[]*endpoint.Endpoint{
{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}},
},
false,
},
{
"annotated node with matching annotation filter returns endpoint",
"service.beta.kubernetes.io/external-traffic in (Global, OnlyLocal)",
"",
"node1",
[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
map[string]string{},
map[string]string{
"service.beta.kubernetes.io/external-traffic": "OnlyLocal",
},
[]*endpoint.Endpoint{
{RecordType: "A", DNSName: "node1", Targets: endpoint.Targets{"1.2.3.4"}},
},
false,
},
{
"annotated node with non-matching annotation filter returns endpoint",
"service.beta.kubernetes.io/external-traffic in (Global, OnlyLocal)",
"",
"node1",
[]v1.NodeAddress{{v1.NodeExternalIP, "1.2.3.4"}},
map[string]string{},
map[string]string{
"service.beta.kubernetes.io/external-traffic": "SomethingElse",
},
[]*endpoint.Endpoint{},
false,
},
} {
t.Run(tc.title, func(t *testing.T) {
// Create a Kubernetes testing client
......@@ -171,6 +229,7 @@ func testNodeSourceEndpoints(t *testing.T) {
// Create our object under test and get the endpoints.
client, err := NewNodeSource(
kubernetes,
tc.annotationFilter,
tc.fqdnTemplate,
)
require.NoError(t, err)
......
......@@ -158,7 +158,7 @@ func BuildWithConfig(source string, p ClientGenerator, cfg *Config) (Source, err
if err != nil {
return nil, err
}
return NewNodeSource(client, cfg.FQDNTemplate)
return NewNodeSource(client, cfg.AnnotationFilter, cfg.FQDNTemplate)
case "service":
client, err := p.KubeClient()
if err != nil {
......
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