Add Type to ServiceSpec: ClusterIP or LoadBalancer

This commit is contained in:
Justin Santa Barbara 2015-05-22 17:49:26 -04:00
parent 3884d5fc59
commit 973c2e4819
37 changed files with 434 additions and 146 deletions

View File

@ -629,6 +629,7 @@ _kubectl_expose()
flags+=("--target-port=")
flags+=("--template=")
two_word_flags+=("-t")
flags+=("--type=")
must_have_one_flag=()
must_have_one_flag+=("--port=")

View File

@ -12,7 +12,7 @@ selector for a new Service on the specified port. If no labels are specified, th
re-use the labels from the resource it exposes.
```
kubectl expose RESOURCE NAME --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--public-ip=ip] [--create-external-load-balancer=bool]
kubectl expose RESOURCE NAME --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--public-ip=ip] [--type=type]
```
### Examples
@ -32,7 +32,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream
```
--container-port="": Synonym for --target-port
--create-external-load-balancer=false: If true, create an external load balancer for this service. Implementation is cloud provider dependent. Default is 'false'.
--create-external-load-balancer=false: If true, create an external load balancer for this service (trumped by --type). Implementation is cloud provider dependent. Default is 'false'.
--dry-run=false: If true, only print the object that would be sent, without creating it.
--generator="service/v1": The name of the API generator to use. Default is 'service/v1'.
-h, --help=false: help for expose
@ -48,6 +48,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream
--selector="": A label selector to use for this service. If empty (the default) infer the selector from the replication controller.
--target-port="": Name or number for the port on the container that the service should direct traffic to. Optional.
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]
--type="": Type for this service: ClusterIP, NodePort, or LoadBalancer. Default is 'ClusterIP' unless --create-external-load-balancer is specified.
```
### Options inherited from parent commands

View File

@ -28,7 +28,7 @@ re\-use the labels from the resource it exposes.
.PP
\fB\-\-create\-external\-load\-balancer\fP=false
If true, create an external load balancer for this service. Implementation is cloud provider dependent. Default is 'false'.
If true, create an external load balancer for this service (trumped by \-\-type). Implementation is cloud provider dependent. Default is 'false'.
.PP
\fB\-\-dry\-run\fP=false
@ -91,6 +91,10 @@ re\-use the labels from the resource it exposes.
Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [
\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]
.PP
\fB\-\-type\fP=""
Type for this service: ClusterIP, NodePort, or LoadBalancer. Default is 'ClusterIP' unless \-\-create\-external\-load\-balancer is specified.
.SH OPTIONS INHERITED FROM PARENT COMMANDS
.PP

View File

@ -35,7 +35,7 @@ On some platforms (for example Google Compute Engine) the kubectl command can in
to do this run:
```bash
kubectl expose rc my-nginx --port=80 --create-external-load-balancer
kubectl expose rc my-nginx --port=80 --type=LoadBalancer
```
This should print the service that has been created, and map an external IP address to the service.

View File

@ -35,6 +35,7 @@ func makeValidService() api.Service {
Spec: api.ServiceSpec{
Selector: map[string]string{"key": "val"},
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{Name: "p", Protocol: "TCP", Port: 8675}},
},
}

View File

@ -186,6 +186,10 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
types := []api.ServiceAffinity{api.ServiceAffinityClientIP, api.ServiceAffinityNone}
*p = types[c.Rand.Intn(len(types))]
},
func(p *api.ServiceType, c fuzz.Continue) {
types := []api.ServiceType{api.ServiceTypeClusterIP, api.ServiceTypeLoadBalancer}
*p = types[c.Rand.Intn(len(types))]
},
func(ct *api.Container, c fuzz.Continue) {
c.FuzzNoCustom(ct) // fuzz self without calling this function again
ct.TerminationMessagePath = "/" + ct.TerminationMessagePath // Must be non-empty

View File

@ -1011,6 +1011,20 @@ const (
ServiceAffinityNone ServiceAffinity = "None"
)
// Service Type string describes ingress methods for a service
type ServiceType string
const (
// ServiceTypeClusterIP means a service will only be accessible inside the
// cluster, via the portal IP.
ServiceTypeClusterIP ServiceType = "ClusterIP"
// ServiceTypeLoadBalancer means a service will be exposed via an
// external load balancer (if the cloud provider supports it), in addition
// to 'NodePort' type.
ServiceTypeLoadBalancer ServiceType = "LoadBalancer"
)
// ServiceStatus represents the current status of a service
type ServiceStatus struct {
// LoadBalancer contains the current status of the load-balancer,
@ -1054,8 +1068,9 @@ type ServiceSpec struct {
// None can be specified for headless services when proxying is not required
PortalIP string `json:"portalIP,omitempty"`
// CreateExternalLoadBalancer indicates whether a load balancer should be created for this service.
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty"`
// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer
Type ServiceType `json:"type,omitempty"`
// PublicIPs are used by external load balancers, or can be set by
// users to handle external traffic that arrives at a node.
// For load balancers, the publicIP will usually be the IP address of the load balancer,

View File

@ -2097,7 +2097,7 @@ func convert_api_ServiceSpec_To_v1_ServiceSpec(in *api.ServiceSpec, out *Service
out.Selector = nil
}
out.PortalIP = in.PortalIP
out.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.Type = ServiceType(in.Type)
if in.PublicIPs != nil {
out.PublicIPs = make([]string, len(in.PublicIPs))
for i := range in.PublicIPs {
@ -4352,7 +4352,7 @@ func convert_v1_ServiceSpec_To_api_ServiceSpec(in *ServiceSpec, out *api.Service
out.Selector = nil
}
out.PortalIP = in.PortalIP
out.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.Type = api.ServiceType(in.Type)
if in.PublicIPs != nil {
out.PublicIPs = make([]string, len(in.PublicIPs))
for i := range in.PublicIPs {

View File

@ -75,6 +75,9 @@ func addDefaultingFuncs() {
if obj.SessionAffinity == "" {
obj.SessionAffinity = ServiceAffinityNone
}
if obj.Type == "" {
obj.Type = ServiceTypeClusterIP
}
for i := range obj.Ports {
sp := &obj.Ports[i]
if sp.Protocol == "" {

View File

@ -233,7 +233,10 @@ func TestSetDefaultService(t *testing.T) {
obj2 := roundTrip(t, runtime.Object(svc))
svc2 := obj2.(*versioned.Service)
if svc2.Spec.SessionAffinity != versioned.ServiceAffinityNone {
t.Errorf("Expected default sesseion affinity type:%s, got: %s", versioned.ServiceAffinityNone, svc2.Spec.SessionAffinity)
t.Errorf("Expected default session affinity type:%s, got: %s", versioned.ServiceAffinityNone, svc2.Spec.SessionAffinity)
}
if svc2.Spec.Type != versioned.ServiceTypeClusterIP {
t.Errorf("Expected default type:%s, got: %s", versioned.ServiceTypeClusterIP, svc2.Spec.Type)
}
}

View File

@ -993,6 +993,20 @@ const (
ServiceAffinityNone ServiceAffinity = "None"
)
// Service Type string describes ingress methods for a service
type ServiceType string
const (
// ServiceTypeClusterIP means a service will only be accessible inside the
// cluster, via the portal IP.
ServiceTypeClusterIP ServiceType = "ClusterIP"
// ServiceTypeLoadBalancer means a service will be exposed via an
// external load balancer (if the cloud provider supports it), in addition
// to 'NodePort' type.
ServiceTypeLoadBalancer ServiceType = "LoadBalancer"
)
// ServiceStatus represents the current status of a service
type ServiceStatus struct {
// LoadBalancer contains the current status of the load-balancer,
@ -1034,8 +1048,8 @@ type ServiceSpec struct {
// None can be specified for headless services when proxying is not required
PortalIP string `json:"portalIP,omitempty description: IP address of the service; usually assigned by the system; if specified, it will be allocated to the service if unused, and creation of the service will fail otherwise; cannot be updated; 'None' can be specified for a headless service when proxying is not required"`
// CreateExternalLoadBalancer indicates whether a load balancer should be created for this service.
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" description:"set up a cloud-provider-specific load balancer on an external IP"`
// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer
Type ServiceType `json:"type,omitempty" description:"type of this service; must be ClusterIP, NodePort, or LoadBalancer; defaults to ClusterIP"`
// PublicIPs are used by external load balancers, or can be set by
// users to handle external traffic that arrives at a node.

View File

@ -780,7 +780,6 @@ func addConversionFuncs() {
if err := s.Convert(&in.Spec.Selector, &out.Selector, 0); err != nil {
return err
}
out.CreateExternalLoadBalancer = in.Spec.CreateExternalLoadBalancer
out.PublicIPs = in.Spec.PublicIPs
out.PortalIP = in.Spec.PortalIP
if err := s.Convert(&in.Spec.SessionAffinity, &out.SessionAffinity, 0); err != nil {
@ -791,6 +790,10 @@ func addConversionFuncs() {
return err
}
if err := s.Convert(&in.Spec.Type, &out.Type, 0); err != nil {
return err
}
return nil
},
func(in *Service, out *api.Service, s conversion.Scope) error {
@ -827,7 +830,6 @@ func addConversionFuncs() {
if err := s.Convert(&in.Selector, &out.Spec.Selector, 0); err != nil {
return err
}
out.Spec.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.Spec.PublicIPs = in.PublicIPs
out.Spec.PortalIP = in.PortalIP
if err := s.Convert(&in.SessionAffinity, &out.Spec.SessionAffinity, 0); err != nil {
@ -850,6 +852,18 @@ func addConversionFuncs() {
return err
}
typeIn := in.Type
if typeIn == "" {
if in.CreateExternalLoadBalancer {
typeIn = ServiceTypeLoadBalancer
} else {
typeIn = ServiceTypeClusterIP
}
}
if err := s.Convert(&typeIn, &out.Spec.Type, 0); err != nil {
return err
}
return nil
},

View File

@ -76,6 +76,15 @@ func addDefaultingFuncs() {
if obj.SessionAffinity == "" {
obj.SessionAffinity = ServiceAffinityNone
}
if obj.Type == "" {
if obj.CreateExternalLoadBalancer {
obj.Type = ServiceTypeLoadBalancer
} else {
obj.Type = ServiceTypeClusterIP
}
} else if obj.Type == ServiceTypeLoadBalancer {
obj.CreateExternalLoadBalancer = true
}
for i := range obj.Ports {
sp := &obj.Ports[i]
if sp.Protocol == "" {

View File

@ -150,7 +150,20 @@ func TestSetDefaultService(t *testing.T) {
t.Errorf("Expected default protocol :%s, got: %s", versioned.ProtocolTCP, svc2.Protocol)
}
if svc2.SessionAffinity != versioned.ServiceAffinityNone {
t.Errorf("Expected default sesseion affinity type:%s, got: %s", versioned.ServiceAffinityNone, svc2.SessionAffinity)
t.Errorf("Expected default session affinity type:%s, got: %s", versioned.ServiceAffinityNone, svc2.SessionAffinity)
}
if svc2.Type != versioned.ServiceTypeClusterIP {
t.Errorf("Expected default type:%s, got: %s", versioned.ServiceTypeClusterIP, svc2.Type)
}
}
func TestSetDefaultServiceWithLoadbalancer(t *testing.T) {
svc := &versioned.Service{}
svc.CreateExternalLoadBalancer = true
obj2 := roundTrip(t, runtime.Object(svc))
svc2 := obj2.(*versioned.Service)
if svc2.Type != versioned.ServiceTypeLoadBalancer {
t.Errorf("Expected default type:%s, got: %s", versioned.ServiceTypeLoadBalancer, svc2.Type)
}
}

View File

@ -835,6 +835,20 @@ const (
ServiceAffinityNone ServiceAffinity = "None"
)
// Service Type string describes ingress methods for a service
type ServiceType string
const (
// ServiceTypeClusterIP means a service will only be accessible inside the
// cluster, via the portal IP.
ServiceTypeClusterIP ServiceType = "ClusterIP"
// ServiceTypeLoadBalancer means a service will be exposed via an
// external load balancer (if the cloud provider supports it), in addition
// to 'NodePort' type.
ServiceTypeLoadBalancer ServiceType = "LoadBalancer"
)
const (
// PortalIPNone - do not assign a portal IP
// no proxying required and no environment variables should be created for pods
@ -873,6 +887,9 @@ type Service struct {
// An external load balancer should be set up via the cloud-provider
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" description:"set up a cloud-provider-specific load balancer on an external IP"`
// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer
Type ServiceType `json:"type,omitempty" description:"type of this service; must be ClusterIP, NodePort, or LoadBalancer; defaults to ClusterIP"`
// PublicIPs are used by external load balancers, or can be set by
// users to handle external traffic that arrives at a node.
PublicIPs []string `json:"publicIPs,omitempty" description:"externally visible IPs (e.g. load balancers) that should be proxied to this service"`

View File

@ -702,7 +702,6 @@ func addConversionFuncs() {
if err := s.Convert(&in.Spec.Selector, &out.Selector, 0); err != nil {
return err
}
out.CreateExternalLoadBalancer = in.Spec.CreateExternalLoadBalancer
out.PublicIPs = in.Spec.PublicIPs
out.PortalIP = in.Spec.PortalIP
if err := s.Convert(&in.Spec.SessionAffinity, &out.SessionAffinity, 0); err != nil {
@ -713,6 +712,10 @@ func addConversionFuncs() {
return err
}
if err := s.Convert(&in.Spec.Type, &out.Type, 0); err != nil {
return err
}
return nil
},
func(in *Service, out *api.Service, s conversion.Scope) error {
@ -749,7 +752,6 @@ func addConversionFuncs() {
if err := s.Convert(&in.Selector, &out.Spec.Selector, 0); err != nil {
return err
}
out.Spec.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.Spec.PublicIPs = in.PublicIPs
out.Spec.PortalIP = in.PortalIP
if err := s.Convert(&in.SessionAffinity, &out.Spec.SessionAffinity, 0); err != nil {
@ -772,6 +774,18 @@ func addConversionFuncs() {
return err
}
typeIn := in.Type
if typeIn == "" {
if in.CreateExternalLoadBalancer {
typeIn = ServiceTypeLoadBalancer
} else {
typeIn = ServiceTypeClusterIP
}
}
if err := s.Convert(&typeIn, &out.Spec.Type, 0); err != nil {
return err
}
return nil
},

View File

@ -77,6 +77,15 @@ func addDefaultingFuncs() {
if obj.SessionAffinity == "" {
obj.SessionAffinity = ServiceAffinityNone
}
if obj.Type == "" {
if obj.CreateExternalLoadBalancer {
obj.Type = ServiceTypeLoadBalancer
} else {
obj.Type = ServiceTypeClusterIP
}
} else if obj.Type == ServiceTypeLoadBalancer {
obj.CreateExternalLoadBalancer = true
}
for i := range obj.Ports {
sp := &obj.Ports[i]
if sp.Protocol == "" {

View File

@ -150,7 +150,20 @@ func TestSetDefaultService(t *testing.T) {
t.Errorf("Expected default protocol :%s, got: %s", versioned.ProtocolTCP, svc2.Protocol)
}
if svc2.SessionAffinity != versioned.ServiceAffinityNone {
t.Errorf("Expected default sesseion affinity type:%s, got: %s", versioned.ServiceAffinityNone, svc2.SessionAffinity)
t.Errorf("Expected default session affinity type:%s, got: %s", versioned.ServiceAffinityNone, svc2.SessionAffinity)
}
if svc2.Type != versioned.ServiceTypeClusterIP {
t.Errorf("Expected default type:%s, got: %s", versioned.ServiceTypeClusterIP, svc2.Type)
}
}
func TestSetDefaultServiceWithLoadbalancer(t *testing.T) {
svc := &versioned.Service{}
svc.CreateExternalLoadBalancer = true
obj2 := roundTrip(t, runtime.Object(svc))
svc2 := obj2.(*versioned.Service)
if svc2.Type != versioned.ServiceTypeLoadBalancer {
t.Errorf("Expected default type:%s, got: %s", versioned.ServiceTypeLoadBalancer, svc2.Type)
}
}

View File

@ -837,6 +837,20 @@ const (
ServiceAffinityNone ServiceAffinity = "None"
)
// Service Type string describes ingress methods for a service
type ServiceType string
const (
// ServiceTypeClusterIP means a service will only be accessible inside the
// cluster, via the portal IP.
ServiceTypeClusterIP ServiceType = "ClusterIP"
// ServiceTypeLoadBalancer means a service will be exposed via an
// external load balancer (if the cloud provider supports it), in addition
// to 'NodePort' type.
ServiceTypeLoadBalancer ServiceType = "LoadBalancer"
)
const (
// PortalIPNone - do not assign a portal IP
// no proxying required and no environment variables should be created for pods
@ -877,6 +891,9 @@ type Service struct {
// An external load balancer should be set up via the cloud-provider
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" description:"set up a cloud-provider-specific load balancer on an external IP"`
// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer
Type ServiceType `json:"type,omitempty" description:"type of this service; must be ClusterIP, NodePort, or LoadBalancer; defaults to ClusterIP"`
// PublicIPs are used by external load balancers, or can be set by
// users to handle external traffic that arrives at a node.
PublicIPs []string `json:"publicIPs,omitempty" description:"externally visible IPs (e.g. load balancers) that should be proxied to this service"`

View File

@ -29,6 +29,8 @@ func addConversionFuncs() {
err := api.Scheme.AddConversionFuncs(
convert_v1beta3_Container_To_api_Container,
convert_api_Container_To_v1beta3_Container,
convert_v1beta3_ServiceSpec_To_api_ServiceSpec,
convert_api_ServiceSpec_To_v1beta3_ServiceSpec,
)
if err != nil {
// If one of the conversion functions is malformed, detect it immediately.
@ -329,3 +331,92 @@ func convert_api_Container_To_v1beta3_Container(in *api.Container, out *Containe
}
return nil
}
func convert_v1beta3_ServiceSpec_To_api_ServiceSpec(in *ServiceSpec, out *api.ServiceSpec, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*ServiceSpec))(in)
}
if in.Ports != nil {
out.Ports = make([]api.ServicePort, len(in.Ports))
for i := range in.Ports {
if err := convert_v1beta3_ServicePort_To_api_ServicePort(&in.Ports[i], &out.Ports[i], s); err != nil {
return err
}
}
} else {
out.Ports = nil
}
if in.Selector != nil {
out.Selector = make(map[string]string)
for key, val := range in.Selector {
out.Selector[key] = val
}
} else {
out.Selector = nil
}
out.PortalIP = in.PortalIP
typeIn := in.Type
if typeIn == "" {
if in.CreateExternalLoadBalancer {
typeIn = ServiceTypeLoadBalancer
} else {
typeIn = ServiceTypeClusterIP
}
}
if err := s.Convert(&typeIn, &out.Type, 0); err != nil {
return err
}
if in.PublicIPs != nil {
out.PublicIPs = make([]string, len(in.PublicIPs))
for i := range in.PublicIPs {
out.PublicIPs[i] = in.PublicIPs[i]
}
} else {
out.PublicIPs = nil
}
out.SessionAffinity = api.ServiceAffinity(in.SessionAffinity)
return nil
}
func convert_api_ServiceSpec_To_v1beta3_ServiceSpec(in *api.ServiceSpec, out *ServiceSpec, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.ServiceSpec))(in)
}
if in.Ports != nil {
out.Ports = make([]ServicePort, len(in.Ports))
for i := range in.Ports {
if err := convert_api_ServicePort_To_v1beta3_ServicePort(&in.Ports[i], &out.Ports[i], s); err != nil {
return err
}
}
} else {
out.Ports = nil
}
if in.Selector != nil {
out.Selector = make(map[string]string)
for key, val := range in.Selector {
out.Selector[key] = val
}
} else {
out.Selector = nil
}
out.PortalIP = in.PortalIP
if err := s.Convert(&in.Type, &out.Type, 0); err != nil {
return err
}
out.CreateExternalLoadBalancer = in.Type == api.ServiceTypeLoadBalancer
if in.PublicIPs != nil {
out.PublicIPs = make([]string, len(in.PublicIPs))
for i := range in.PublicIPs {
out.PublicIPs[i] = in.PublicIPs[i]
}
} else {
out.PublicIPs = nil
}
out.SessionAffinity = ServiceAffinity(in.SessionAffinity)
return nil
}

View File

@ -2013,42 +2013,6 @@ func convert_api_ServicePort_To_v1beta3_ServicePort(in *api.ServicePort, out *Se
return nil
}
func convert_api_ServiceSpec_To_v1beta3_ServiceSpec(in *api.ServiceSpec, out *ServiceSpec, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.ServiceSpec))(in)
}
if in.Ports != nil {
out.Ports = make([]ServicePort, len(in.Ports))
for i := range in.Ports {
if err := convert_api_ServicePort_To_v1beta3_ServicePort(&in.Ports[i], &out.Ports[i], s); err != nil {
return err
}
}
} else {
out.Ports = nil
}
if in.Selector != nil {
out.Selector = make(map[string]string)
for key, val := range in.Selector {
out.Selector[key] = val
}
} else {
out.Selector = nil
}
out.PortalIP = in.PortalIP
out.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
if in.PublicIPs != nil {
out.PublicIPs = make([]string, len(in.PublicIPs))
for i := range in.PublicIPs {
out.PublicIPs[i] = in.PublicIPs[i]
}
} else {
out.PublicIPs = nil
}
out.SessionAffinity = ServiceAffinity(in.SessionAffinity)
return nil
}
func convert_api_ServiceStatus_To_v1beta3_ServiceStatus(in *api.ServiceStatus, out *ServiceStatus, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.ServiceStatus))(in)
@ -4237,42 +4201,6 @@ func convert_v1beta3_ServicePort_To_api_ServicePort(in *ServicePort, out *api.Se
return nil
}
func convert_v1beta3_ServiceSpec_To_api_ServiceSpec(in *ServiceSpec, out *api.ServiceSpec, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*ServiceSpec))(in)
}
if in.Ports != nil {
out.Ports = make([]api.ServicePort, len(in.Ports))
for i := range in.Ports {
if err := convert_v1beta3_ServicePort_To_api_ServicePort(&in.Ports[i], &out.Ports[i], s); err != nil {
return err
}
}
} else {
out.Ports = nil
}
if in.Selector != nil {
out.Selector = make(map[string]string)
for key, val := range in.Selector {
out.Selector[key] = val
}
} else {
out.Selector = nil
}
out.PortalIP = in.PortalIP
out.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
if in.PublicIPs != nil {
out.PublicIPs = make([]string, len(in.PublicIPs))
for i := range in.PublicIPs {
out.PublicIPs[i] = in.PublicIPs[i]
}
} else {
out.PublicIPs = nil
}
out.SessionAffinity = api.ServiceAffinity(in.SessionAffinity)
return nil
}
func convert_v1beta3_ServiceStatus_To_api_ServiceStatus(in *ServiceStatus, out *api.ServiceStatus, s conversion.Scope) error {
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*ServiceStatus))(in)
@ -4577,7 +4505,6 @@ func init() {
convert_api_ServiceAccount_To_v1beta3_ServiceAccount,
convert_api_ServiceList_To_v1beta3_ServiceList,
convert_api_ServicePort_To_v1beta3_ServicePort,
convert_api_ServiceSpec_To_v1beta3_ServiceSpec,
convert_api_ServiceStatus_To_v1beta3_ServiceStatus,
convert_api_Service_To_v1beta3_Service,
convert_api_StatusCause_To_v1beta3_StatusCause,
@ -4690,7 +4617,6 @@ func init() {
convert_v1beta3_ServiceAccount_To_api_ServiceAccount,
convert_v1beta3_ServiceList_To_api_ServiceList,
convert_v1beta3_ServicePort_To_api_ServicePort,
convert_v1beta3_ServiceSpec_To_api_ServiceSpec,
convert_v1beta3_ServiceStatus_To_api_ServiceStatus,
convert_v1beta3_Service_To_api_Service,
convert_v1beta3_StatusCause_To_api_StatusCause,

View File

@ -73,6 +73,15 @@ func addDefaultingFuncs() {
if obj.SessionAffinity == "" {
obj.SessionAffinity = ServiceAffinityNone
}
if obj.Type == "" {
if obj.CreateExternalLoadBalancer {
obj.Type = ServiceTypeLoadBalancer
} else {
obj.Type = ServiceTypeClusterIP
}
} else if obj.Type == ServiceTypeLoadBalancer {
obj.CreateExternalLoadBalancer = true
}
for i := range obj.Ports {
sp := &obj.Ports[i]
if sp.Protocol == "" {

View File

@ -160,7 +160,20 @@ func TestSetDefaultService(t *testing.T) {
obj2 := roundTrip(t, runtime.Object(svc))
svc2 := obj2.(*versioned.Service)
if svc2.Spec.SessionAffinity != versioned.ServiceAffinityNone {
t.Errorf("Expected default sesseion affinity type:%s, got: %s", versioned.ServiceAffinityNone, svc2.Spec.SessionAffinity)
t.Errorf("Expected default session affinity type:%s, got: %s", versioned.ServiceAffinityNone, svc2.Spec.SessionAffinity)
}
if svc2.Spec.Type != versioned.ServiceTypeClusterIP {
t.Errorf("Expected default type:%s, got: %s", versioned.ServiceTypeClusterIP, svc2.Spec.Type)
}
}
func TestSetDefaultServiceWithLoadbalancer(t *testing.T) {
svc := &versioned.Service{}
svc.Spec.CreateExternalLoadBalancer = true
obj2 := roundTrip(t, runtime.Object(svc))
svc2 := obj2.(*versioned.Service)
if svc2.Spec.Type != versioned.ServiceTypeLoadBalancer {
t.Errorf("Expected default type:%s, got: %s", versioned.ServiceTypeLoadBalancer, svc2.Spec.Type)
}
}

View File

@ -997,6 +997,20 @@ const (
ServiceAffinityNone ServiceAffinity = "None"
)
// Service Type string describes ingress methods for a service
type ServiceType string
const (
// ServiceTypeClusterIP means a service will only be accessible inside the
// cluster, via the portal IP.
ServiceTypeClusterIP ServiceType = "ClusterIP"
// ServiceTypeLoadBalancer means a service will be exposed via an
// external load balancer (if the cloud provider supports it), in addition
// to 'NodePort' type.
ServiceTypeLoadBalancer ServiceType = "LoadBalancer"
)
// ServiceStatus represents the current status of a service
type ServiceStatus struct {
// LoadBalancer contains the current status of the load-balancer,
@ -1041,6 +1055,9 @@ type ServiceSpec struct {
// CreateExternalLoadBalancer indicates whether a load balancer should be created for this service.
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" description:"set up a cloud-provider-specific load balancer on an external IP"`
// Type determines how the service will be exposed. Valid options: ClusterIP, NodePort, LoadBalancer
Type ServiceType `json:"type,omitempty" description:"type of this service; must be ClusterIP, NodePort, or LoadBalancer; defaults to ClusterIP"`
// PublicIPs are used by external load balancers, or can be set by
// users to handle external traffic that arrives at a node.
PublicIPs []string `json:"publicIPs,omitempty" description:"externally visible IPs (e.g. load balancers) that should be proxied to this service"`

View File

@ -1032,6 +1032,8 @@ func ValidatePodTemplateUpdate(newPod, oldPod *api.PodTemplate) errs.ValidationE
}
var supportedSessionAffinityType = util.NewStringSet(string(api.ServiceAffinityClientIP), string(api.ServiceAffinityNone))
var supportedServiceType = util.NewStringSet(string(api.ServiceTypeClusterIP),
string(api.ServiceTypeLoadBalancer))
// ValidateService tests if required fields in the service are set.
func ValidateService(service *api.Service) errs.ValidationErrorList {
@ -1070,7 +1072,13 @@ func ValidateService(service *api.Service) errs.ValidationErrorList {
}
}
if service.Spec.CreateExternalLoadBalancer {
if service.Spec.Type == "" {
allErrs = append(allErrs, errs.NewFieldRequired("spec.type"))
} else if !supportedServiceType.Has(string(service.Spec.Type)) {
allErrs = append(allErrs, errs.NewFieldNotSupported("spec.type", service.Spec.Type))
}
if service.Spec.Type == api.ServiceTypeLoadBalancer {
for i := range service.Spec.Ports {
if service.Spec.Ports[i].Protocol != api.ProtocolTCP {
allErrs = append(allErrs, errs.NewFieldInvalid("spec.ports", service.Spec.Ports[i], "cannot create an external load balancer with non-TCP ports"))

View File

@ -1427,6 +1427,7 @@ func makeValidService() api.Service {
Spec: api.ServiceSpec{
Selector: map[string]string{"key": "val"},
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{Name: "p", Protocol: "TCP", Port: 8675}},
},
}
@ -1522,6 +1523,13 @@ func TestValidateService(t *testing.T) {
},
numErrs: 1,
},
{
name: "missing type",
tweakSvc: func(s *api.Service) {
s.Spec.Type = ""
},
numErrs: 1,
},
{
name: "missing ports",
tweakSvc: func(s *api.Service) {
@ -1632,7 +1640,7 @@ func TestValidateService(t *testing.T) {
{
name: "invalid load balancer protocol 1",
tweakSvc: func(s *api.Service) {
s.Spec.CreateExternalLoadBalancer = true
s.Spec.Type = api.ServiceTypeLoadBalancer
s.Spec.Ports[0].Protocol = "UDP"
},
numErrs: 1,
@ -1640,7 +1648,7 @@ func TestValidateService(t *testing.T) {
{
name: "invalid load balancer protocol 2",
tweakSvc: func(s *api.Service) {
s.Spec.CreateExternalLoadBalancer = true
s.Spec.Type = api.ServiceTypeLoadBalancer
s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "UDP"})
},
numErrs: 1,
@ -1683,16 +1691,31 @@ func TestValidateService(t *testing.T) {
numErrs: 0,
},
{
name: "valid external load balancer",
name: "valid visbility - cluster",
tweakSvc: func(s *api.Service) {
s.Spec.CreateExternalLoadBalancer = true
s.Spec.Type = api.ServiceTypeClusterIP
},
numErrs: 0,
},
{
name: "valid visbility - loadbalancer",
tweakSvc: func(s *api.Service) {
s.Spec.Type = api.ServiceTypeLoadBalancer
},
numErrs: 0,
},
{
name: "valid type loadbalancer 2 ports",
tweakSvc: func(s *api.Service) {
s.Spec.Type = api.ServiceTypeLoadBalancer
s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP"})
},
numErrs: 0,
},
{
name: "valid external load balancer 2 ports",
tweakSvc: func(s *api.Service) {
s.Spec.CreateExternalLoadBalancer = true
s.Spec.Type = api.ServiceTypeLoadBalancer
s.Spec.Ports = append(s.Spec.Ports, api.ServicePort{Name: "q", Port: 12345, Protocol: "TCP"})
},
numErrs: 0,
@ -2458,6 +2481,20 @@ func TestValidateServiceUpdate(t *testing.T) {
},
numErrs: 1,
},
{
name: "change type",
tweakSvc: func(oldSvc, newSvc *api.Service) {
newSvc.Spec.Type = api.ServiceTypeLoadBalancer
},
numErrs: 0,
},
{
name: "remove type",
tweakSvc: func(oldSvc, newSvc *api.Service) {
newSvc.Spec.Type = ""
},
numErrs: 1,
},
}
for _, tc := range testCases {

View File

@ -231,7 +231,7 @@ func (s *ServiceController) createLoadBalancerIfNeeded(namespacedName types.Name
if cachedService != nil {
// If the service already exists but needs to be updated, delete it so that
// we can recreate it cleanly.
if cachedService.Spec.CreateExternalLoadBalancer {
if wantsExternalLoadBalancer(cachedService) {
glog.Infof("Deleting existing load balancer for service %s that needs an updated load balancer.", namespacedName)
if err := s.balancer.EnsureTCPLoadBalancerDeleted(s.loadBalancerName(cachedService), s.zone.Region); err != nil {
return err, retryable
@ -256,7 +256,7 @@ func (s *ServiceController) createLoadBalancerIfNeeded(namespacedName types.Name
}
}
if !service.Spec.CreateExternalLoadBalancer {
if !wantsExternalLoadBalancer(service) {
glog.Infof("Not creating LB for service %s that doesn't want one.", namespacedName)
return nil, notRetryable
}
@ -404,10 +404,10 @@ func (s *serviceCache) delete(serviceName string) {
}
func needsUpdate(oldService *api.Service, newService *api.Service) bool {
if !oldService.Spec.CreateExternalLoadBalancer && !newService.Spec.CreateExternalLoadBalancer {
if !wantsExternalLoadBalancer(oldService) && !wantsExternalLoadBalancer(newService) {
return false
}
if oldService.Spec.CreateExternalLoadBalancer != newService.Spec.CreateExternalLoadBalancer {
if wantsExternalLoadBalancer(oldService) != wantsExternalLoadBalancer(newService) {
return true
}
if !portsEqual(oldService, newService) || oldService.Spec.SessionAffinity != newService.Spec.SessionAffinity {
@ -561,7 +561,7 @@ func (s *ServiceController) updateLoadBalancerHosts(services []*cachedService, h
// Updates the external load balancer of a service, assuming we hold the mutex
// associated with the service.
func (s *ServiceController) lockedUpdateLoadBalancerHosts(service *api.Service, hosts []string) error {
if !service.Spec.CreateExternalLoadBalancer {
if !wantsExternalLoadBalancer(service) {
return nil
}
@ -579,3 +579,7 @@ func (s *ServiceController) lockedUpdateLoadBalancerHosts(service *api.Service,
}
return err
}
func wantsExternalLoadBalancer(service *api.Service) bool {
return service.Spec.Type == api.ServiceTypeLoadBalancer
}

View File

@ -28,8 +28,8 @@ import (
const region = "us-central"
func newService(name string, uid types.UID, external bool) *api.Service {
return &api.Service{ObjectMeta: api.ObjectMeta{Name: name, Namespace: "namespace", UID: uid}, Spec: api.ServiceSpec{CreateExternalLoadBalancer: external}}
func newService(name string, uid types.UID, serviceType api.ServiceType) *api.Service {
return &api.Service{ObjectMeta: api.ObjectMeta{Name: name, Namespace: "namespace", UID: uid}, Spec: api.ServiceSpec{Type: serviceType}}
}
func TestCreateExternalLoadBalancer(t *testing.T) {
@ -45,7 +45,7 @@ func TestCreateExternalLoadBalancer(t *testing.T) {
Namespace: "default",
},
Spec: api.ServiceSpec{
CreateExternalLoadBalancer: false,
Type: api.ServiceTypeClusterIP,
},
},
expectErr: false,
@ -62,7 +62,7 @@ func TestCreateExternalLoadBalancer(t *testing.T) {
Port: 80,
Protocol: api.ProtocolUDP,
}},
CreateExternalLoadBalancer: true,
Type: api.ServiceTypeLoadBalancer,
},
},
expectErr: true,
@ -79,7 +79,7 @@ func TestCreateExternalLoadBalancer(t *testing.T) {
Port: 80,
Protocol: api.ProtocolTCP,
}},
CreateExternalLoadBalancer: true,
Type: api.ServiceTypeLoadBalancer,
},
},
expectErr: false,
@ -144,15 +144,15 @@ func TestUpdateNodesInExternalLoadBalancer(t *testing.T) {
{
// Services do not have external load balancers: no calls should be made.
services: []*api.Service{
newService("s0", "111", false),
newService("s1", "222", false),
newService("s0", "111", api.ServiceTypeClusterIP),
newService("s1", "222", api.ServiceTypeClusterIP),
},
expectedUpdateCalls: nil,
},
{
// Services does have an external load balancer: one call should be made.
services: []*api.Service{
newService("s0", "333", true),
newService("s0", "333", api.ServiceTypeLoadBalancer),
},
expectedUpdateCalls: []fake_cloud.FakeUpdateBalancerCall{
{Name: "a333", Region: region, Hosts: []string{"node0", "node1", "node73"}},
@ -161,9 +161,9 @@ func TestUpdateNodesInExternalLoadBalancer(t *testing.T) {
{
// Three services have an external load balancer: three calls.
services: []*api.Service{
newService("s0", "444", true),
newService("s1", "555", true),
newService("s2", "666", true),
newService("s0", "444", api.ServiceTypeLoadBalancer),
newService("s1", "555", api.ServiceTypeLoadBalancer),
newService("s2", "666", api.ServiceTypeLoadBalancer),
},
expectedUpdateCalls: []fake_cloud.FakeUpdateBalancerCall{
{Name: "a444", Region: region, Hosts: []string{"node0", "node1", "node73"}},
@ -174,10 +174,10 @@ func TestUpdateNodesInExternalLoadBalancer(t *testing.T) {
{
// Two services have an external load balancer and two don't: two calls.
services: []*api.Service{
newService("s0", "777", false),
newService("s1", "888", true),
newService("s3", "999", true),
newService("s4", "123", false),
newService("s0", "777", api.ServiceTypeClusterIP),
newService("s1", "888", api.ServiceTypeLoadBalancer),
newService("s3", "999", api.ServiceTypeLoadBalancer),
newService("s4", "123", api.ServiceTypeClusterIP),
},
expectedUpdateCalls: []fake_cloud.FakeUpdateBalancerCall{
{Name: "a888", Region: region, Hosts: []string{"node0", "node1", "node73"}},
@ -187,7 +187,7 @@ func TestUpdateNodesInExternalLoadBalancer(t *testing.T) {
{
// One service has an external load balancer and one is nil: one call.
services: []*api.Service{
newService("s0", "234", true),
newService("s0", "234", api.ServiceTypeLoadBalancer),
nil,
},
expectedUpdateCalls: []fake_cloud.FakeUpdateBalancerCall{

View File

@ -46,7 +46,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream`
func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "expose RESOURCE NAME --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--public-ip=ip] [--create-external-load-balancer=bool]",
Use: "expose RESOURCE NAME --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--public-ip=ip] [--type=type]",
Short: "Take a replicated application and expose it as Kubernetes Service",
Long: expose_long,
Example: expose_example,
@ -60,7 +60,8 @@ func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().String("protocol", "TCP", "The network protocol for the service to be created. Default is 'tcp'.")
cmd.Flags().Int("port", -1, "The port that the service should serve on. Required.")
cmd.MarkFlagRequired("port")
cmd.Flags().Bool("create-external-load-balancer", false, "If true, create an external load balancer for this service. Implementation is cloud provider dependent. Default is 'false'.")
cmd.Flags().String("type", "", "Type for this service: ClusterIP, NodePort, or LoadBalancer. Default is 'ClusterIP' unless --create-external-load-balancer is specified.")
cmd.Flags().Bool("create-external-load-balancer", false, "If true, create an external load balancer for this service (trumped by --type). Implementation is cloud provider dependent. Default is 'false'.")
cmd.Flags().String("selector", "", "A label selector to use for this service. If empty (the default) infer the selector from the replication controller.")
cmd.Flags().StringP("labels", "l", "", "Labels to apply to the service created by this call.")
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without creating it.")
@ -161,6 +162,9 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
}
params["labels"] = kubectl.MakeLabels(labels)
}
if v := cmdutil.GetFlagString(cmd, "type"); v != "" {
params["type"] = v
}
err = kubectl.ValidateParams(names, params)
if err != nil {
return err

View File

@ -68,6 +68,7 @@ func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList)
ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
Spec: api.ServiceSpec{
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
},
},
},

View File

@ -141,6 +141,7 @@ func TestMerge(t *testing.T) {
expected: &api.Service{
Spec: api.ServiceSpec{
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
},
},
},
@ -157,6 +158,7 @@ func TestMerge(t *testing.T) {
expected: &api.Service{
Spec: api.ServiceSpec{
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
Selector: map[string]string{
"version": "v2",
},

View File

@ -505,6 +505,7 @@ func describeService(service *api.Service, endpoints *api.Endpoints, events *api
fmt.Fprintf(out, "Name:\t%s\n", service.Name)
fmt.Fprintf(out, "Labels:\t%s\n", formatLabels(service.Labels))
fmt.Fprintf(out, "Selector:\t%s\n", formatLabels(service.Spec.Selector))
fmt.Fprintf(out, "Type:\t%s\n", service.Spec.Type)
fmt.Fprintf(out, "IP:\t%s\n", service.Spec.PortalIP)
if len(service.Status.LoadBalancer.Ingress) > 0 {
list := buildIngressString(service.Status.LoadBalancer.Ingress)

View File

@ -35,6 +35,7 @@ func (ServiceGenerator) ParamNames() []GeneratorParam {
{"labels", false},
{"public-ip", false},
{"create-external-load-balancer", false},
{"type", false},
{"protocol", false},
{"container-port", false}, // alias of target-port
{"target-port", false},
@ -102,10 +103,13 @@ func (ServiceGenerator) Generate(params map[string]string) (runtime.Object, erro
service.Spec.Ports[0].TargetPort = util.NewIntOrStringFromInt(port)
}
if params["create-external-load-balancer"] == "true" {
service.Spec.CreateExternalLoadBalancer = true
service.Spec.Type = api.ServiceTypeLoadBalancer
}
if len(params["public-ip"]) != 0 {
service.Spec.PublicIPs = []string{params["public-ip"]}
}
if len(params["type"]) != 0 {
service.Spec.Type = api.ServiceType(params["type"])
}
return &service, nil
}

View File

@ -175,8 +175,8 @@ func TestGenerateService(t *testing.T) {
TargetPort: util.NewIntOrStringFromString("foobar"),
},
},
PublicIPs: []string{"1.2.3.4"},
CreateExternalLoadBalancer: true,
PublicIPs: []string{"1.2.3.4"},
Type: api.ServiceTypeLoadBalancer,
},
},
},

View File

@ -601,6 +601,7 @@ func TestEtcdUpdateService(t *testing.T) {
"baz": "bar",
},
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
},
}
_, err := registry.UpdateService(ctx, &testService)

View File

@ -68,6 +68,7 @@ func TestServiceRegistryCreate(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -109,6 +110,7 @@ func TestServiceStorageValidatesCreate(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -120,6 +122,7 @@ func TestServiceStorageValidatesCreate(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Protocol: api.ProtocolTCP,
}},
@ -162,6 +165,7 @@ func TestServiceRegistryUpdate(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz2"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -205,6 +209,7 @@ func TestServiceStorageValidatesUpdate(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -216,6 +221,7 @@ func TestServiceStorageValidatesUpdate(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"ThisSelectorFailsValidation": "ok"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -240,9 +246,9 @@ func TestServiceRegistryExternalService(t *testing.T) {
svc := &api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
CreateExternalLoadBalancer: true,
SessionAffinity: api.ServiceAffinityNone,
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeLoadBalancer,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -270,6 +276,7 @@ func TestServiceRegistryDelete(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -289,9 +296,9 @@ func TestServiceRegistryDeleteExternal(t *testing.T) {
svc := &api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
CreateExternalLoadBalancer: true,
SessionAffinity: api.ServiceAffinityNone,
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeLoadBalancer,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -313,9 +320,9 @@ func TestServiceRegistryUpdateExternalService(t *testing.T) {
svc1 := &api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
CreateExternalLoadBalancer: false,
SessionAffinity: api.ServiceAffinityNone,
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -328,7 +335,7 @@ func TestServiceRegistryUpdateExternalService(t *testing.T) {
// Modify load balancer to be external.
svc2 := deepCloneService(svc1)
svc2.Spec.CreateExternalLoadBalancer = true
svc2.Spec.Type = api.ServiceTypeLoadBalancer
if _, _, err := storage.Update(ctx, svc2); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
@ -349,9 +356,9 @@ func TestServiceRegistryUpdateMultiPortExternalService(t *testing.T) {
svc1 := &api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
CreateExternalLoadBalancer: true,
SessionAffinity: api.ServiceAffinityNone,
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeLoadBalancer,
Ports: []api.ServicePort{{
Name: "p",
Port: 6502,
@ -491,6 +498,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -512,6 +520,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -541,6 +550,7 @@ func TestServiceRegistryIPAllocation(t *testing.T) {
Selector: map[string]string{"bar": "baz"},
PortalIP: testIP,
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -566,6 +576,7 @@ func TestServiceRegistryIPReallocation(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -582,13 +593,17 @@ func TestServiceRegistryIPReallocation(t *testing.T) {
t.Errorf("Unexpected PortalIP: %s", created_service_1.Spec.PortalIP)
}
rest.Delete(ctx, created_service_1.Name)
_, err := rest.Delete(ctx, created_service_1.Name)
if err != nil {
t.Errorf("Unexpected error deleting service: %v", err)
}
svc2 := &api.Service{
ObjectMeta: api.ObjectMeta{Name: "bar"},
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -614,6 +629,7 @@ func TestServiceRegistryIPUpdate(t *testing.T) {
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -649,15 +665,15 @@ func TestServiceRegistryIPUpdate(t *testing.T) {
}
}
func TestServiceRegistryIPExternalLoadBalancer(t *testing.T) {
func TestServiceRegistryIPLoadBalancer(t *testing.T) {
rest, _ := NewTestREST(t, nil)
svc := &api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
Spec: api.ServiceSpec{
Selector: map[string]string{"bar": "baz"},
CreateExternalLoadBalancer: true,
SessionAffinity: api.ServiceAffinityNone,
Selector: map[string]string{"bar": "baz"},
SessionAffinity: api.ServiceAffinityNone,
Type: api.ServiceTypeLoadBalancer,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -730,6 +746,7 @@ func TestCreate(t *testing.T) {
Selector: map[string]string{"bar": "baz"},
PortalIP: "None",
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,
@ -746,6 +763,7 @@ func TestCreate(t *testing.T) {
Selector: map[string]string{"bar": "baz"},
PortalIP: "invalid",
SessionAffinity: "None",
Type: api.ServiceTypeClusterIP,
Ports: []api.ServicePort{{
Port: 6502,
Protocol: api.ProtocolTCP,

View File

@ -253,7 +253,7 @@ var _ = Describe("Services", func() {
It("should be able to create a functioning external load balancer", func() {
if !providerIs("gce", "gke") {
By(fmt.Sprintf("Skipping service external load balancer test; uses createExternalLoadBalancer, a (gce|gke) feature"))
By(fmt.Sprintf("Skipping service external load balancer test; uses ServiceTypeLoadBalancer, a (gce|gke) feature"))
return
}
@ -272,7 +272,7 @@ var _ = Describe("Services", func() {
Port: 80,
TargetPort: util.NewIntOrStringFromInt(80),
}},
CreateExternalLoadBalancer: true,
Type: api.ServiceTypeLoadBalancer,
},
}
@ -353,7 +353,7 @@ var _ = Describe("Services", func() {
It("should correctly serve identically named services in different namespaces on different external IP addresses", func() {
if !providerIs("gce", "gke") {
By(fmt.Sprintf("Skipping service namespace collision test; uses createExternalLoadBalancer, a (gce|gke) feature"))
By(fmt.Sprintf("Skipping service namespace collision test; uses ServiceTypeLoadBalancer, a (gce|gke) feature"))
return
}
@ -370,7 +370,7 @@ var _ = Describe("Services", func() {
Port: 80,
TargetPort: util.NewIntOrStringFromInt(80),
}},
CreateExternalLoadBalancer: true,
Type: api.ServiceTypeLoadBalancer,
},
}