forked from kubernetes-sigs/external-dns
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
338 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
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 endpoint | ||
|
||
import ( | ||
"net" | ||
"strings" | ||
|
||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
// TargetFilterInterface defines the interface to select matching targets for a specific provider or runtime | ||
type TargetFilterInterface interface { | ||
Match(target string) bool | ||
IsConfigured() bool | ||
} | ||
|
||
// TargetNetFilter holds a lists of valid target names | ||
type TargetNetFilter struct { | ||
// FilterNets define what targets to match | ||
FilterNets []*net.IPNet | ||
// excludeNets define what targets not to match | ||
excludeNets []*net.IPNet | ||
} | ||
|
||
// prepareTargetFilters provides consistent trimming for filters/exclude params | ||
func prepareTargetFilters(filters []string) []*net.IPNet { | ||
fs := make([]*net.IPNet, 0) | ||
|
||
for _, filter := range filters { | ||
filter = strings.TrimSpace(filter) | ||
|
||
_, filterNet, err := net.ParseCIDR(filter) | ||
if err != nil { | ||
log.Errorf("Invalid target net filter: %s", filter) | ||
|
||
continue | ||
} | ||
|
||
fs = append(fs, filterNet) | ||
} | ||
return fs | ||
} | ||
|
||
// NewTargetNetFilterWithExclusions returns a new TargetNetFilter, given a list of matches and exclusions | ||
func NewTargetNetFilterWithExclusions(targetFilterNets []string, excludeNets []string) TargetNetFilter { | ||
return TargetNetFilter{FilterNets: prepareTargetFilters(targetFilterNets), excludeNets: prepareTargetFilters(excludeNets)} | ||
} | ||
|
||
// NewTargetNetFilter returns a new TargetNetFilter given a comma separated list of targets | ||
func NewTargetNetFilter(targetFilterNets []string) TargetNetFilter { | ||
return TargetNetFilter{FilterNets: prepareTargetFilters(targetFilterNets)} | ||
} | ||
|
||
// Match checks whether a target can be found in the TargetNetFilter. | ||
func (tf TargetNetFilter) Match(target string) bool { | ||
return matchTargetNetFilter(tf.FilterNets, target, true) && !matchTargetNetFilter(tf.excludeNets, target, false) | ||
} | ||
|
||
// matchTargetNetFilter determines if any `filters` match `target`. | ||
// If no `filters` are provided, behavior depends on `emptyval` | ||
// (empty `tf.filters` matches everything, while empty `tf.exclude` excludes nothing) | ||
func matchTargetNetFilter(filters []*net.IPNet, target string, emptyval bool) bool { | ||
if len(filters) == 0 { | ||
return emptyval | ||
} | ||
|
||
for _, filter := range filters { | ||
ip := net.ParseIP(target) | ||
|
||
if filter.Contains(ip) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
// IsConfigured returns true if TargetFilter is configured, false otherwise | ||
func (tf TargetNetFilter) IsConfigured() bool { | ||
if len(tf.FilterNets) == 1 { | ||
return tf.FilterNets[0].Network() != "" | ||
} | ||
return len(tf.FilterNets) > 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/* | ||
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 endpoint | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
type targetFilterTest struct { | ||
targetFilter []string | ||
exclusions []string | ||
targets []string | ||
expected bool | ||
} | ||
|
||
var targetFilterTests = []targetFilterTest{ | ||
{ | ||
[]string{"10.0.0.0/8"}, | ||
[]string{}, | ||
[]string{"10.1.2.3"}, | ||
true, | ||
}, | ||
{ | ||
[]string{" 10.0.0.0/8 "}, | ||
[]string{}, | ||
[]string{"10.1.2.3"}, | ||
true, | ||
}, | ||
{ | ||
[]string{"0"}, | ||
[]string{}, | ||
[]string{"10.1.2.3"}, | ||
true, | ||
}, | ||
{ | ||
[]string{"10.0.0.0/8"}, | ||
[]string{}, | ||
[]string{"1.1.1.1"}, | ||
false, | ||
}, | ||
{ | ||
[]string{}, | ||
[]string{"10.0.0.0/8"}, | ||
[]string{"1.1.1.1"}, | ||
true, | ||
}, | ||
{ | ||
[]string{}, | ||
[]string{"10.0.0.0/8"}, | ||
[]string{"10.1.2.3"}, | ||
false, | ||
}, | ||
} | ||
|
||
func TestTargetFilterMatch(t *testing.T) { | ||
for i, tt := range targetFilterTests { | ||
if len(tt.exclusions) > 0 { | ||
t.Logf("NewTargetFilter() doesn't support exclusions - skipping test %+v", tt) | ||
continue | ||
} | ||
targetFilter := NewTargetNetFilter(tt.targetFilter) | ||
for _, target := range tt.targets { | ||
assert.Equal(t, tt.expected, targetFilter.Match(target), "should not fail: %v in test-case #%v", target, i) | ||
} | ||
} | ||
} | ||
|
||
func TestTargetFilterWithExclusions(t *testing.T) { | ||
for i, tt := range targetFilterTests { | ||
if len(tt.exclusions) == 0 { | ||
tt.exclusions = append(tt.exclusions, "") | ||
} | ||
targetFilter := NewTargetNetFilterWithExclusions(tt.targetFilter, tt.exclusions) | ||
for _, target := range tt.targets { | ||
assert.Equal(t, tt.expected, targetFilter.Match(target), "should not fail: %v in test-case #%v", target, i) | ||
} | ||
} | ||
} | ||
|
||
func TestTargetFilterMatchWithEmptyFilter(t *testing.T) { | ||
for _, tt := range targetFilterTests { | ||
targetFilter := TargetNetFilter{} | ||
for i, target := range tt.targets { | ||
assert.True(t, targetFilter.Match(target), "should not fail: %v in test-case #%v", target, i) | ||
} | ||
} | ||
} | ||
|
||
func TestMatchTargetFilterReturnsProperEmptyVal(t *testing.T) { | ||
emptyFilters := []string{} | ||
assert.Equal(t, true, matchFilter(emptyFilters, "sometarget.com", true)) | ||
assert.Equal(t, false, matchFilter(emptyFilters, "sometarget.com", false)) | ||
} | ||
|
||
func TestTargetFilterIsConfigured(t *testing.T) { | ||
for _, tt := range []struct { | ||
filters []string | ||
exclude []string | ||
expected bool | ||
}{ | ||
{ | ||
[]string{""}, | ||
[]string{""}, | ||
false, | ||
}, | ||
{ | ||
[]string{" "}, | ||
[]string{" "}, | ||
false, | ||
}, | ||
{ | ||
[]string{"", ""}, | ||
[]string{""}, | ||
false, | ||
}, | ||
{ | ||
[]string{"10/8"}, | ||
[]string{" "}, | ||
false, | ||
}, | ||
{ | ||
[]string{"10.0.0.0/8"}, | ||
[]string{" "}, | ||
true, | ||
}, | ||
{ | ||
[]string{" 10.0.0.0/8 "}, | ||
[]string{" ignored "}, | ||
true, | ||
}, | ||
} { | ||
t.Run("test IsConfigured", func(t *testing.T) { | ||
tf := NewTargetNetFilterWithExclusions(tt.filters, tt.exclude) | ||
assert.Equal(t, tt.expected, tf.IsConfigured()) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
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 ( | ||
"context" | ||
|
||
"sigs.k8s.io/external-dns/endpoint" | ||
) | ||
|
||
// targetFilterSource is a Source that removes endpoints matching the target filter from its wrapped source. | ||
type targetFilterSource struct { | ||
source Source | ||
targetFilter endpoint.TargetFilterInterface | ||
} | ||
|
||
// NewTargetFilterSource creates a new targetFilterSource wrapping the provided Source. | ||
func NewTargetFilterSource(source Source, targetFilter endpoint.TargetFilterInterface) Source { | ||
return &targetFilterSource{source: source, targetFilter: targetFilter} | ||
} | ||
|
||
// Endpoints collects endpoints from its wrapped source and returns | ||
// them without targets matching the target filter. | ||
func (ms *targetFilterSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { | ||
result := []*endpoint.Endpoint{} | ||
|
||
endpoints, err := ms.source.Endpoints(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, ep := range endpoints { | ||
filteredTargets := []string{} | ||
|
||
for _, t := range ep.Targets { | ||
if ms.targetFilter.Match(t) { | ||
filteredTargets = append(filteredTargets, t) | ||
} | ||
} | ||
|
||
ep.Targets = filteredTargets | ||
|
||
result = append(result, ep) | ||
} | ||
|
||
return result, nil | ||
} | ||
|
||
func (ms *targetFilterSource) AddEventHandler(ctx context.Context, handler func()) { | ||
ms.source.AddEventHandler(ctx, handler) | ||
} |