Merge pull request #33319 from juanvallejo/jvallejo/add-option-to-set-nodeport

Automatic merge from submit-queue

Add option to set a service nodeport

**Release note**:
```release-note
Add kubectl --node-port option for specifying the service nodeport
```

This patch adds the option to set a nodeport when creating a NodePort
service. In case of a port allocation error due to a specified port
being out of the valid range, the error now includes the valid
range. If a `--node-port` value is not specified, it defaults to zero, in
which case the allocator will default to its current behavior of
assigning an available port.

This patch also adds a new helper function in `cmd/util/helpers.go` to
retrieve `Int32` cobra flags.

**Example**
```
# create a nodeport service with an invalid port
$ kubectl create service nodeport mynodeport --tcp=8080:7777 --node-port=1
The Service "mynodeport" is invalid: spec.ports[0].nodePort: Invalid
value: 1: provided port is not in the valid range. Valid ports range
from 30000-32767

# create a nodeport service with a valid port
$ kubectl create service nodeport mynodeport --tcp=8080:7777 --node-port=30000
service "mynodeport" created

# create a nodeport service with a port already in use
$ kubectl create service nodeport mynodeport --tcp=8080:7777 --node-port=30000
The Service "mynodeport" is invalid: spec.ports[0].nodePort: Invalid value: 3000: provided port is already allocated

$ kubectl describe service mynodeport
Name:                   mynodeport
Namespace:              default
Labels:                 app=mynodeport
Selector:               app=mynodeport
Type:                   NodePort
IP:                     172.30.81.254
Port:                   8080-7777       8080/TCP
NodePort:               8080-7777       30000/TCP
Endpoints:              <none>
Session Affinity:       None
No events.
```

@fabianofranz
This commit is contained in:
Kubernetes Submit Queue 2016-10-05 15:00:32 -07:00 committed by GitHub
commit 05192d9d57
7 changed files with 35 additions and 12 deletions

View File

@ -384,6 +384,7 @@ node-monitor-period
node-name node-name
node-os-distro node-os-distro
node-path-override node-path-override
node-port
node-startup-grace-period node-startup-grace-period
node-status-update-frequency node-status-update-frequency
node-sync-period node-sync-period

View File

@ -134,6 +134,7 @@ func NewCmdCreateServiceNodePort(f *cmdutil.Factory, cmdOut io.Writer) *cobra.Co
cmdutil.AddValidateFlags(cmd) cmdutil.AddValidateFlags(cmd)
cmdutil.AddPrinterFlags(cmd) cmdutil.AddPrinterFlags(cmd)
cmdutil.AddGeneratorFlags(cmd, cmdutil.ServiceNodePortGeneratorV1Name) cmdutil.AddGeneratorFlags(cmd, cmdutil.ServiceNodePortGeneratorV1Name)
cmd.Flags().Int("node-port", 0, "Port used to expose the service on each node in a cluster.")
addPortFlags(cmd) addPortFlags(cmd)
return cmd return cmd
} }
@ -152,6 +153,7 @@ func CreateServiceNodePort(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Comm
TCP: cmdutil.GetFlagStringSlice(cmd, "tcp"), TCP: cmdutil.GetFlagStringSlice(cmd, "tcp"),
Type: api.ServiceTypeNodePort, Type: api.ServiceTypeNodePort,
ClusterIP: "", ClusterIP: "",
NodePort: cmdutil.GetFlagInt(cmd, "node-port"),
} }
default: default:
return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName)) return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not supported.", generatorName))

View File

@ -296,7 +296,7 @@ func isWatch(cmd *cobra.Command) bool {
func GetFlagString(cmd *cobra.Command, flag string) string { func GetFlagString(cmd *cobra.Command, flag string) string {
s, err := cmd.Flags().GetString(flag) s, err := cmd.Flags().GetString(flag)
if err != nil { if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err) glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
} }
return s return s
} }
@ -305,7 +305,7 @@ func GetFlagString(cmd *cobra.Command, flag string) string {
func GetFlagStringSlice(cmd *cobra.Command, flag string) []string { func GetFlagStringSlice(cmd *cobra.Command, flag string) []string {
s, err := cmd.Flags().GetStringSlice(flag) s, err := cmd.Flags().GetStringSlice(flag)
if err != nil { if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err) glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
} }
return s return s
} }
@ -331,7 +331,7 @@ func GetWideFlag(cmd *cobra.Command) bool {
func GetFlagBool(cmd *cobra.Command, flag string) bool { func GetFlagBool(cmd *cobra.Command, flag string) bool {
b, err := cmd.Flags().GetBool(flag) b, err := cmd.Flags().GetBool(flag)
if err != nil { if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err) glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
} }
return b return b
} }
@ -340,7 +340,7 @@ func GetFlagBool(cmd *cobra.Command, flag string) bool {
func GetFlagInt(cmd *cobra.Command, flag string) int { func GetFlagInt(cmd *cobra.Command, flag string) int {
i, err := cmd.Flags().GetInt(flag) i, err := cmd.Flags().GetInt(flag)
if err != nil { if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err) glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
} }
return i return i
} }
@ -349,7 +349,7 @@ func GetFlagInt(cmd *cobra.Command, flag string) int {
func GetFlagInt64(cmd *cobra.Command, flag string) int64 { func GetFlagInt64(cmd *cobra.Command, flag string) int64 {
i, err := cmd.Flags().GetInt64(flag) i, err := cmd.Flags().GetInt64(flag)
if err != nil { if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err) glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
} }
return i return i
} }
@ -357,7 +357,7 @@ func GetFlagInt64(cmd *cobra.Command, flag string) int64 {
func GetFlagDuration(cmd *cobra.Command, flag string) time.Duration { func GetFlagDuration(cmd *cobra.Command, flag string) time.Duration {
d, err := cmd.Flags().GetDuration(flag) d, err := cmd.Flags().GetDuration(flag)
if err != nil { if err != nil {
glog.Fatalf("err accessing flag %s for command %s: %v", flag, cmd.Name(), err) glog.Fatalf("error accessing flag %s for command %s: %v", flag, cmd.Name(), err)
} }
return d return d
} }

View File

@ -31,6 +31,7 @@ type ServiceCommonGeneratorV1 struct {
TCP []string TCP []string
Type api.ServiceType Type api.ServiceType
ClusterIP string ClusterIP string
NodePort int
} }
type ServiceClusterIPGeneratorV1 struct { type ServiceClusterIPGeneratorV1 struct {
@ -56,6 +57,7 @@ func (ServiceNodePortGeneratorV1) ParamNames() []GeneratorParam {
return []GeneratorParam{ return []GeneratorParam{
{"name", true}, {"name", true},
{"tcp", true}, {"tcp", true},
{"nodeport", true},
} }
} }
func (ServiceLoadBalancerGeneratorV1) ParamNames() []GeneratorParam { func (ServiceLoadBalancerGeneratorV1) ParamNames() []GeneratorParam {
@ -174,12 +176,14 @@ func (s ServiceCommonGeneratorV1) StructuredGenerate() (runtime.Object, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
portName := strings.Replace(tcpString, ":", "-", -1) portName := strings.Replace(tcpString, ":", "-", -1)
ports = append(ports, api.ServicePort{ ports = append(ports, api.ServicePort{
Name: portName, Name: portName,
Port: port, Port: port,
TargetPort: targetPort, TargetPort: targetPort,
Protocol: api.Protocol("TCP"), Protocol: api.Protocol("TCP"),
NodePort: int32(s.NodePort),
}) })
} }

View File

@ -37,11 +37,18 @@ type Interface interface {
var ( var (
ErrFull = errors.New("range is full") ErrFull = errors.New("range is full")
ErrNotInRange = errors.New("provided port is not in the valid range")
ErrAllocated = errors.New("provided port is already allocated") ErrAllocated = errors.New("provided port is already allocated")
ErrMismatchedNetwork = errors.New("the provided port range does not match the current port range") ErrMismatchedNetwork = errors.New("the provided port range does not match the current port range")
) )
type ErrNotInRange struct {
ValidPorts string
}
func (e *ErrNotInRange) Error() string {
return fmt.Sprintf("provided port is not in the valid range. The range of valid ports is %s", e.ValidPorts)
}
type PortAllocator struct { type PortAllocator struct {
portRange net.PortRange portRange net.PortRange
@ -82,7 +89,9 @@ func (r *PortAllocator) Free() int {
func (r *PortAllocator) Allocate(port int) error { func (r *PortAllocator) Allocate(port int) error {
ok, offset := r.contains(port) ok, offset := r.contains(port)
if !ok { if !ok {
return ErrNotInRange // include valid port range in error
validPorts := r.portRange.String()
return &ErrNotInRange{validPorts}
} }
allocated, err := r.alloc.Allocate(offset) allocated, err := r.alloc.Allocate(offset)

View File

@ -73,16 +73,23 @@ func TestAllocate(t *testing.T) {
if err := r.Release(released); err != nil { if err := r.Release(released); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := r.Allocate(1); err != ErrNotInRange {
err = r.Allocate(1)
if _, ok := err.(*ErrNotInRange); !ok {
t.Fatal(err) t.Fatal(err)
} }
if err := r.Allocate(10001); err != ErrAllocated { if err := r.Allocate(10001); err != ErrAllocated {
t.Fatal(err) t.Fatal(err)
} }
if err := r.Allocate(20000); err != ErrNotInRange {
err = r.Allocate(20000)
if _, ok := err.(*ErrNotInRange); !ok {
t.Fatal(err) t.Fatal(err)
} }
if err := r.Allocate(10201); err != ErrNotInRange {
err = r.Allocate(10201)
if _, ok := err.(*ErrNotInRange); !ok {
t.Fatal(err) t.Fatal(err)
} }
if f := r.Free(); f != 1 { if f := r.Free(); f != 1 {

View File

@ -114,7 +114,7 @@ func (c *Repair) runOnce() error {
// TODO: send event // TODO: send event
// port is broken, reallocate // port is broken, reallocate
runtime.HandleError(fmt.Errorf("the port %d for service %s/%s was assigned to multiple services; please recreate", port, svc.Name, svc.Namespace)) runtime.HandleError(fmt.Errorf("the port %d for service %s/%s was assigned to multiple services; please recreate", port, svc.Name, svc.Namespace))
case portallocator.ErrNotInRange: case err.(*portallocator.ErrNotInRange):
// TODO: send event // TODO: send event
// port is broken, reallocate // port is broken, reallocate
runtime.HandleError(fmt.Errorf("the port %d for service %s/%s is not within the port range %v; please recreate", port, svc.Name, svc.Namespace, c.portRange)) runtime.HandleError(fmt.Errorf("the port %d for service %s/%s is not within the port range %v; please recreate", port, svc.Name, svc.Namespace, c.portRange))