Change:
<!-- This form is for bug reports and feature requests ONLY! If you're looking for help check and the . --Is this a BUG REPORT or FEATURE REQUEST?: /kind feature What happened: Implement this TODO: What you expected to happen: Start kube-proxy with a 'beginPort+offset' format port range configuration. This is needed when I want to allocate specific numbers of port from a host without extra manual calculation. Environment: Kubernetes version (use ): Cloud provider or hardware configuration: OS (e.g. from /etc/os-release): Kernel (e.g. ): Install tools: Others:
/sig api-machinery
/sig network Does anyone besides kube-proxy use this?
/assign

Discussion:
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.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).") <del> 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.") </del> <ins> 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.") </ins> 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.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].")

return fmt.Sprintf("%d-%d", pr.Base, pr.Base+pr.Size-1) } <del> // Set parses a string of the form "min-max", inclusive at both ends, and </del> <ins> // Set parses a string of the form "value", "min-max", or "min+offset", inclusive at both ends, and </ins> // sets the PortRange from it.  This is part of the flag.Value and pflag.Value // interfaces. func (pr *PortRange) Set(value string) error { <del> value = strings.TrimSpace(value) </del> <ins> const ( SinglePortNotation = 1 << iota HyphenNotation PlusNotation ) </ins> <del> // TODO: Accept "80" syntax // TODO: Accept "80+8" syntax </del> <ins> value = strings.TrimSpace(value) hyphenIndex := strings.Index(value, "-") plusIndex := strings.Index(value, "+") </ins> if value == "" { pr.Base = 0

return nil } <del> hyphenIndex := strings.Index(value, "-") if hyphenIndex == -1 { return fmt.Errorf("expected hyphen in port range") </del> <ins> var err error var low, high int var notation int if plusIndex == -1 && hyphenIndex == -1 { notation |= SinglePortNotation } if hyphenIndex != -1 { notation |= HyphenNotation } if plusIndex != -1 { notation |= PlusNotation </ins> } <del> var err error var low int var high int low, err = strconv.Atoi(value[:hyphenIndex]) if err == nil { </del> <ins> switch notation { case SinglePortNotation: 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]) if err != nil { return err } </ins> high, err = strconv.Atoi(value[hyphenIndex+1:]) <del> } if err != nil { return fmt.Errorf("unable to parse port range: %s: %v", value, err) </del> <ins> 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 { return err } high = low + offset default: return fmt.Errorf("unable to parse port range: %s", value) </ins> } if low > 65535 || high > 65535 {

{" 100-200 ", true, "100-200", 200, 201}, {"0-0", true, "0-0", 0, 1}, {"", true, "", -1, 0}, <del> {"100", false, "", -1, -1}, </del> <ins> {"100", true, "100-100", 100, 101}, </ins> {"100 - 200", false, "", -1, -1}, {"-100", false, "", -1, -1}, {"100-", false, "", -1, -1}, {"200-100", false, "", -1, -1}, {"60000-70000", false, "", -1, -1}, {"70000-80000", false, "", -1, -1}, <ins> {"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}, </ins> } for i := range testCases {

t.Errorf("expected success, got %q", err) continue } else if err == nil && tc.success == false { <del> t.Errorf("expected failure") </del> <ins> t.Errorf("expected failure %#v", testCases[i]) </ins> continue } else if tc.success { if f.String() != tc.expected {