Unverified Commit 5be9578e authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #58731 from yue9944882/feature/beginport-and-offset-expr-port-range

Automatic merge from submit-queue (batch tested with PRs 60457, 60331, 54970, 58731, 60562). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. implement begin-port+offset port range parsing **What this PR does / why we need it**: Allocate specific numbers of port from a host by indicating a begin port and its offsets. This can be used for kube-proxy configuration. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes #58730 **Special notes for your reviewer**: **Release note**: ```release-note "beginPort+offset" format support for port range which affects kube-proxy only ```
parents 8684e581 68ad76bf
...@@ -141,7 +141,7 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) { ...@@ -141,7 +141,7 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&o.config.ResourceContainer, "resource-container", o.config.ResourceContainer, "Absolute name of the resource-only container to create and run the Kube-proxy in (Default: /kube-proxy).") fs.StringVar(&o.config.ResourceContainer, "resource-container", o.config.ResourceContainer, "Absolute name of the resource-only container to create and run the Kube-proxy in (Default: /kube-proxy).")
fs.MarkDeprecated("resource-container", "This feature will be removed in a later release.") fs.MarkDeprecated("resource-container", "This feature will be removed in a later release.")
fs.StringVar(&o.config.ClientConnection.KubeConfigFile, "kubeconfig", o.config.ClientConnection.KubeConfigFile, "Path to kubeconfig file with authorization information (the master location is set by the master flag).") fs.StringVar(&o.config.ClientConnection.KubeConfigFile, "kubeconfig", o.config.ClientConnection.KubeConfigFile, "Path to kubeconfig file with authorization information (the master location is set by the master flag).")
fs.Var(componentconfig.PortRangeVar{Val: &o.config.PortRange}, "proxy-port-range", "Range of host ports (beginPort-endPort, inclusive) that may be consumed in order to proxy service traffic. If unspecified (0-0) then ports will be randomly chosen.") fs.Var(componentconfig.PortRangeVar{Val: &o.config.PortRange}, "proxy-port-range", "Range of host ports (beginPort-endPort, single port or beginPort+offset, inclusive) that may be consumed in order to proxy service traffic. If (unspecified, 0, or 0-0) then ports will be randomly chosen.")
fs.StringVar(&o.config.HostnameOverride, "hostname-override", o.config.HostnameOverride, "If non-empty, will use this string as identification instead of the actual hostname.") fs.StringVar(&o.config.HostnameOverride, "hostname-override", o.config.HostnameOverride, "If non-empty, will use this string as identification instead of the actual hostname.")
fs.Var(&o.config.Mode, "proxy-mode", "Which proxy mode to use: 'userspace' (older) or 'iptables' (faster) or 'ipvs' (experimental). If blank, use the best-available proxy (currently iptables). If the iptables proxy is selected, regardless of how, but the system's kernel or iptables versions are insufficient, this always falls back to the userspace proxy.") fs.Var(&o.config.Mode, "proxy-mode", "Which proxy mode to use: 'userspace' (older) or 'iptables' (faster) or 'ipvs' (experimental). If blank, use the best-available proxy (currently iptables). If the iptables proxy is selected, regardless of how, but the system's kernel or iptables versions are insufficient, this always falls back to the userspace proxy.")
fs.Int32Var(o.config.IPTables.MasqueradeBit, "iptables-masquerade-bit", utilpointer.Int32PtrDerefOr(o.config.IPTables.MasqueradeBit, 14), "If using the pure iptables proxy, the bit of the fwmark space to mark packets requiring SNAT with. Must be within the range [0, 31].") fs.Int32Var(o.config.IPTables.MasqueradeBit, "iptables-masquerade-bit", utilpointer.Int32PtrDerefOr(o.config.IPTables.MasqueradeBit, 14), "If using the pure iptables proxy, the bit of the fwmark space to mark packets requiring SNAT with. Must be within the range [0, 31].")
......
...@@ -43,14 +43,19 @@ func (pr PortRange) String() string { ...@@ -43,14 +43,19 @@ func (pr PortRange) String() string {
return fmt.Sprintf("%d-%d", pr.Base, pr.Base+pr.Size-1) 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 // Set parses a string of the form "value", "min-max", or "min+offset", inclusive at both ends, and
// sets the PortRange from it. This is part of the flag.Value and pflag.Value // sets the PortRange from it. This is part of the flag.Value and pflag.Value
// interfaces. // interfaces.
func (pr *PortRange) Set(value string) error { func (pr *PortRange) Set(value string) error {
value = strings.TrimSpace(value) const (
SinglePortNotation = 1 << iota
HyphenNotation
PlusNotation
)
// TODO: Accept "80" syntax value = strings.TrimSpace(value)
// TODO: Accept "80+8" syntax hyphenIndex := strings.Index(value, "-")
plusIndex := strings.Index(value, "+")
if value == "" { if value == "" {
pr.Base = 0 pr.Base = 0
...@@ -58,20 +63,51 @@ func (pr *PortRange) Set(value string) error { ...@@ -58,20 +63,51 @@ func (pr *PortRange) Set(value string) error {
return nil return nil
} }
hyphenIndex := strings.Index(value, "-") var err error
if hyphenIndex == -1 { var low, high int
return fmt.Errorf("expected hyphen in port range") var notation int
if plusIndex == -1 && hyphenIndex == -1 {
notation |= SinglePortNotation
}
if hyphenIndex != -1 {
notation |= HyphenNotation
}
if plusIndex != -1 {
notation |= PlusNotation
} }
var err error switch notation {
var low int case SinglePortNotation:
var high int var port int
port, err = strconv.Atoi(value)
if err != nil {
return err
}
low = port
high = port
case HyphenNotation:
low, err = strconv.Atoi(value[:hyphenIndex]) low, err = strconv.Atoi(value[:hyphenIndex])
if err == nil { if err != nil {
return err
}
high, err = strconv.Atoi(value[hyphenIndex+1:]) high, err = strconv.Atoi(value[hyphenIndex+1:])
if err != nil {
return err
}
case PlusNotation:
var offset int
low, err = strconv.Atoi(value[:plusIndex])
if err != nil {
return err
} }
offset, err = strconv.Atoi(value[plusIndex+1:])
if err != nil { if err != nil {
return fmt.Errorf("unable to parse port range: %s: %v", value, err) return err
}
high = low + offset
default:
return fmt.Errorf("unable to parse port range: %s", value)
} }
if low > 65535 || high > 65535 { if low > 65535 || high > 65535 {
......
...@@ -34,13 +34,21 @@ func TestPortRange(t *testing.T) { ...@@ -34,13 +34,21 @@ func TestPortRange(t *testing.T) {
{" 100-200 ", true, "100-200", 200, 201}, {" 100-200 ", true, "100-200", 200, 201},
{"0-0", true, "0-0", 0, 1}, {"0-0", true, "0-0", 0, 1},
{"", true, "", -1, 0}, {"", true, "", -1, 0},
{"100", false, "", -1, -1}, {"100", true, "100-100", 100, 101},
{"100 - 200", false, "", -1, -1}, {"100 - 200", false, "", -1, -1},
{"-100", false, "", -1, -1}, {"-100", false, "", -1, -1},
{"100-", false, "", -1, -1}, {"100-", false, "", -1, -1},
{"200-100", false, "", -1, -1}, {"200-100", false, "", -1, -1},
{"60000-70000", false, "", -1, -1}, {"60000-70000", false, "", -1, -1},
{"70000-80000", false, "", -1, -1}, {"70000-80000", false, "", -1, -1},
{"70000+80000", false, "", -1, -1},
{"1+0", true, "1-1", 1, 2},
{"0+0", true, "0-0", 0, 1},
{"1+-1", false, "", -1, -1},
{"1-+1", false, "", -1, -1},
{"100+200", true, "100-300", 300, 301},
{"1+65535", false, "", -1, -1},
{"0+65535", true, "0-65535", 65535, 65536},
} }
for i := range testCases { for i := range testCases {
...@@ -52,7 +60,7 @@ func TestPortRange(t *testing.T) { ...@@ -52,7 +60,7 @@ func TestPortRange(t *testing.T) {
t.Errorf("expected success, got %q", err) t.Errorf("expected success, got %q", err)
continue continue
} else if err == nil && tc.success == false { } else if err == nil && tc.success == false {
t.Errorf("expected failure") t.Errorf("expected failure %#v", testCases[i])
continue continue
} else if tc.success { } else if tc.success {
if f.String() != tc.expected { if f.String() != tc.expected {
......
Markdown is supported
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