From 2457c284ed0b29d0cc1864f3f5e4853c2d46fcf8 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Sat, 16 May 2015 14:37:03 -0400 Subject: [PATCH] Add util.PortRange, so that a port range can be a flag. Format: lo-hi --- pkg/util/port_range.go | 100 ++++++++++++++++++++++++++++++++++++ pkg/util/port_range_test.go | 66 ++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 pkg/util/port_range.go create mode 100644 pkg/util/port_range_test.go diff --git a/pkg/util/port_range.go b/pkg/util/port_range.go new file mode 100644 index 00000000000..cfdde18af30 --- /dev/null +++ b/pkg/util/port_range.go @@ -0,0 +1,100 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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 util + +import ( + "fmt" + "strconv" + "strings" +) + +// PortRange represents a range of TCP/UDP ports. To represent a single port, +// set Size to 1. +type PortRange struct { + Base int + Size int +} + +// Contains tests whether a given port falls within the PortRange. +func (pr *PortRange) Contains(p int) bool { + return (p >= pr.Base) && ((p - pr.Base) < pr.Size) +} + +// String converts the PortRange to a string representation, which can be +// parsed by PortRange.Set or ParsePortRange. +func (pr PortRange) String() string { + if pr.Size == 0 { + return "" + } + return fmt.Sprintf("%d-%d", pr.Base, pr.Base+pr.Size-1) +} + +// Set parses a string of the form "min-max", inclusive at both ends, and +// sets the PortRange from it. This is part of the flag.Value and pflag.Value +// interfaces. +func (pr *PortRange) Set(value string) error { + value = strings.TrimSpace(value) + + // TODO: Accept "80" syntax + // TODO: Accept "80+8" syntax + + if value == "" { + pr.Base = 0 + pr.Size = 0 + return nil + } + + hyphenIndex := strings.Index(value, "-") + if hyphenIndex == -1 { + return fmt.Errorf("expected hyphen in port range") + } + + var err error + var low int + var high int + low, err = strconv.Atoi(value[:hyphenIndex]) + if err == nil { + high, err = strconv.Atoi(value[hyphenIndex+1:]) + } + if err != nil { + return fmt.Errorf("unable to parse port range: %s", value) + } + + if high < low { + return fmt.Errorf("end port cannot be less than start port: %s", value) + } + pr.Base = low + pr.Size = 1 + high - low + return nil +} + +// Type returns a descriptive string about this type. This is part of the +// pflag.Value interface. +func (*PortRange) Type() string { + return "portRange" +} + +// ParsePortRange parses a string of the form "min-max", inclusive at both +// ends, and initializs a new PortRange from it. +func ParsePortRange(value string) (*PortRange, error) { + pr := &PortRange{} + err := pr.Set(value) + if err != nil { + return nil, err + } + return pr, nil +} diff --git a/pkg/util/port_range_test.go b/pkg/util/port_range_test.go new file mode 100644 index 00000000000..f434519695c --- /dev/null +++ b/pkg/util/port_range_test.go @@ -0,0 +1,66 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +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 util + +import ( + "testing" + + flag "github.com/spf13/pflag" +) + +func TestPortRange(t *testing.T) { + testCases := []struct { + input string + success bool + expected string + included int + excluded int + }{ + {"100-200", true, "100-200", 200, 201}, + {" 100-200 ", true, "100-200", 200, 201}, + {"0-0", true, "0-0", 0, 1}, + {"", true, "", -1, 0}, + {"100", false, "", -1, -1}, + {"100 - 200", false, "", -1, -1}, + {"-100", false, "", -1, -1}, + {"100-", false, "", -1, -1}, + } + + for i := range testCases { + tc := &testCases[i] + pr := &PortRange{} + var f flag.Value = pr + err := f.Set(tc.input) + if err != nil && tc.success == true { + t.Errorf("expected success, got %q", err) + continue + } else if err == nil && tc.success == false { + t.Errorf("expected failure") + continue + } else if tc.success { + if f.String() != tc.expected { + t.Errorf("expected %q, got %q", tc.expected, f.String()) + } + if tc.included >= 0 && !pr.Contains(tc.included) { + t.Errorf("expected %q to include %d", f.String(), tc.included) + } + if tc.excluded >= 0 && pr.Contains(tc.excluded) { + t.Errorf("expected %q to exclude %d", f.String(), tc.excluded) + } + } + } +}