mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 12:15:52 +00:00
Add Type to ServiceSpec: ClusterIP or LoadBalancer
This commit is contained in:
parent
3884d5fc59
commit
973c2e4819
@ -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=")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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}},
|
||||
},
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 == "" {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
},
|
||||
|
||||
|
@ -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 == "" {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"`
|
||||
|
@ -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
|
||||
},
|
||||
|
||||
|
@ -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 == "" {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"`
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 == "" {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"`
|
||||
|
@ -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"))
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -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",
|
||||
},
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -601,6 +601,7 @@ func TestEtcdUpdateService(t *testing.T) {
|
||||
"baz": "bar",
|
||||
},
|
||||
SessionAffinity: "None",
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
}
|
||||
_, err := registry.UpdateService(ctx, &testService)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user