-
Notifications
You must be signed in to change notification settings - Fork 303
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1896 from panslava/rework-source-ranges
Reimplement getting load balancer source ranges
- Loading branch information
Showing
7 changed files
with
204 additions
and
9 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
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,90 @@ | ||
package utils | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
v1 "k8s.io/api/core/v1" | ||
"k8s.io/utils/net" | ||
) | ||
|
||
const ( | ||
allowAllIPv4Range = "0.0.0.0/0" | ||
) | ||
|
||
func ServiceSourceRanges(service *v1.Service) ([]string, error) { | ||
ipRanges, err := getAllSourceRanges(service) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if len(ipRanges) == 0 { | ||
return []string{allowAllIPv4Range}, nil | ||
} | ||
return ipRanges.StringSlice(), nil | ||
} | ||
|
||
func getAllSourceRanges(service *v1.Service) (net.IPNetSet, error) { | ||
// if SourceRange field is specified, ignore sourceRange annotation | ||
if len(service.Spec.LoadBalancerSourceRanges) > 0 { | ||
specs := service.Spec.LoadBalancerSourceRanges | ||
for i, spec := range specs { | ||
specs[i] = strings.TrimSpace(spec) | ||
} | ||
ipnets, err := net.ParseIPNets(specs...) | ||
if err != nil { | ||
return nil, NewInvalidSpecLoadBalancerSourceRangesError(specs, err) | ||
} | ||
return ipnets, err | ||
} | ||
|
||
val, ok := service.Annotations[v1.AnnotationLoadBalancerSourceRangesKey] | ||
if !ok { | ||
return nil, nil | ||
} | ||
specs := strings.Split(val, ",") | ||
for i, spec := range specs { | ||
specs[i] = strings.TrimSpace(spec) | ||
} | ||
ipnets, err := net.ParseIPNets(specs...) | ||
if err != nil { | ||
return nil, NewInvalidLoadBalancerSourceRangesAnnotationError(val, err) | ||
} | ||
return ipnets, nil | ||
} | ||
|
||
// InvalidLoadBalancerSourceRangesSpecError is a struct to define error caused by | ||
// User misconfiguration of the Service.Spec.LoadBalancerSourceRanges field. | ||
type InvalidLoadBalancerSourceRangesSpecError struct { | ||
LoadBalancerSourceRangesSpec []string | ||
ParseErr error | ||
} | ||
|
||
func (e *InvalidLoadBalancerSourceRangesSpecError) Error() string { | ||
return fmt.Sprintf("service.Spec.LoadBalancerSourceRanges: %v is not valid. Expecting a list of IP ranges. For example, 10.0.0.0/24. Error msg: %v", e.LoadBalancerSourceRangesSpec, e.ParseErr) | ||
} | ||
|
||
func NewInvalidSpecLoadBalancerSourceRangesError(specLoadBalancerSourceRanges []string, err error) *InvalidLoadBalancerSourceRangesSpecError { | ||
return &InvalidLoadBalancerSourceRangesSpecError{ | ||
specLoadBalancerSourceRanges, | ||
err, | ||
} | ||
} | ||
|
||
// InvalidLoadBalancerSourceRangesAnnotationError is a struct to define error caused by | ||
// User misconfiguration of the Service.Annotations["service.beta.kubernetes.io/load-balancer-source-ranges"] field. | ||
type InvalidLoadBalancerSourceRangesAnnotationError struct { | ||
LoadBalancerSourceRangesAnnotation string | ||
ParseErr error | ||
} | ||
|
||
func (e *InvalidLoadBalancerSourceRangesAnnotationError) Error() string { | ||
return fmt.Sprintf("Service annotation %s: %s is not valid. Expecting a comma-separated list of source IP ranges. For example, 10.0.0.0/24,192.168.2.0/24", v1.AnnotationLoadBalancerSourceRangesKey, e.LoadBalancerSourceRangesAnnotation) | ||
} | ||
|
||
func NewInvalidLoadBalancerSourceRangesAnnotationError(serviceLoadBalancerSourceRangesAnnotation string, err error) *InvalidLoadBalancerSourceRangesAnnotationError { | ||
return &InvalidLoadBalancerSourceRangesAnnotationError{ | ||
serviceLoadBalancerSourceRangesAnnotation, | ||
err, | ||
} | ||
} |
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,90 @@ | ||
package utils | ||
|
||
import ( | ||
"net" | ||
"sort" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
v1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
func TestServiceSourceRanges(t *testing.T) { | ||
testCases := []struct { | ||
desc string | ||
specRanges []string | ||
annotations map[string]string | ||
expectedSourceRanges []string | ||
expectError error | ||
}{ | ||
{ | ||
desc: "Should return allow all for no specs or annotations", | ||
expectedSourceRanges: []string{"0.0.0.0/0"}, | ||
}, | ||
{ | ||
desc: "Should parse ranges from spec", | ||
specRanges: []string{" 192.168.0.1/10", " 132.8.0.1/8 "}, | ||
expectedSourceRanges: []string{"192.128.0.0/10", "132.0.0.0/8"}, // only significant bits are left, spaces are trimmed | ||
}, | ||
{ | ||
desc: "Should parse ranges from annotations, if no spec value", | ||
annotations: map[string]string{ | ||
v1.AnnotationLoadBalancerSourceRangesKey: " 192.168.0.1/10 , 132.8.0.1/8 ", | ||
}, | ||
expectedSourceRanges: []string{"192.128.0.0/10", "132.0.0.0/8"}, // only significant bits are left, spaces are trimmed | ||
}, | ||
{ | ||
desc: "Should ignore annotation if spec is present", | ||
specRanges: []string{"192.168.0.1/10", "132.8.0.1/8"}, | ||
annotations: map[string]string{ | ||
v1.AnnotationLoadBalancerSourceRangesKey: "1.2.3 1.2.3", // should not return error, even if annotation is invalid | ||
}, | ||
expectedSourceRanges: []string{"192.128.0.0/10", "132.0.0.0/8"}, // only significant bits are left | ||
}, | ||
{ | ||
desc: "Should return special error for invalid spec value", | ||
specRanges: []string{"1.0.1.2"}, // wrong CIDR, because no mask | ||
expectError: &InvalidLoadBalancerSourceRangesSpecError{ | ||
LoadBalancerSourceRangesSpec: []string{"1.0.1.2"}, | ||
ParseErr: &net.ParseError{Type: "CIDR address", Text: "1.0.1.2"}, | ||
}, | ||
}, | ||
{ | ||
desc: "Should return special error for invalid annotation value", | ||
annotations: map[string]string{ | ||
v1.AnnotationLoadBalancerSourceRangesKey: "1.2.3.4 1.2.3.4", // should be comma-separated | ||
}, | ||
expectError: &InvalidLoadBalancerSourceRangesAnnotationError{ | ||
LoadBalancerSourceRangesAnnotation: "1.2.3.4 1.2.3.4", | ||
ParseErr: &net.ParseError{Type: "CIDR address", Text: "1.2.3.4 1.2.3.4"}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.desc, func(t *testing.T) { | ||
svc := &v1.Service{ | ||
Spec: v1.ServiceSpec{ | ||
LoadBalancerSourceRanges: tc.specRanges, | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Annotations: tc.annotations, | ||
}, | ||
} | ||
|
||
sourceRanges, err := ServiceSourceRanges(svc) | ||
errDiff := cmp.Diff(tc.expectError, err) | ||
if errDiff != "" { | ||
t.Errorf("Expected error %v, got %v, diff: %v", tc.expectError, err, errDiff) | ||
} | ||
|
||
sort.Strings(tc.expectedSourceRanges) | ||
sort.Strings(sourceRanges) | ||
diff := cmp.Diff(tc.expectedSourceRanges, sourceRanges) | ||
if diff != "" { | ||
t.Errorf("Expected source ranges: %v, got ranges %v, diff: %s", tc.expectedSourceRanges, sourceRanges, diff) | ||
} | ||
}) | ||
} | ||
} |
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