mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-05 23:47:50 +00:00
Allow an empty service
This commit is contained in:
@@ -593,8 +593,10 @@ type ServiceSpec struct {
|
||||
// Optional: Supports "TCP" and "UDP". Defaults to "TCP".
|
||||
Protocol Protocol `json:"protocol,omitempty"`
|
||||
|
||||
// This service will route traffic to pods having labels matching this selector.
|
||||
Selector map[string]string `json:"selector,omitempty"`
|
||||
// This service will route traffic to pods having labels matching this selector. If empty or not present,
|
||||
// the service is assumed to have endpoints set by an external process and Kubernetes will not modify
|
||||
// those endpoints.
|
||||
Selector map[string]string `json:"selector"`
|
||||
|
||||
// PortalIP is usually assigned by the master. If specified by the user
|
||||
// we will try to respect it or else fail the request. This field can
|
||||
|
||||
@@ -192,3 +192,35 @@ func TestMinionListConversionToOld(t *testing.T) {
|
||||
t.Errorf("Expected: %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceEmptySelector(t *testing.T) {
|
||||
// Nil map should be preserved
|
||||
svc := &v1beta1.Service{Selector: nil}
|
||||
data, err := newer.Scheme.EncodeToVersion(svc, "v1beta1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
obj, err := newer.Scheme.Decode(data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
selector := obj.(*newer.Service).Spec.Selector
|
||||
if selector != nil {
|
||||
t.Errorf("unexpected selector: %#v", obj)
|
||||
}
|
||||
|
||||
// Empty map should be preserved
|
||||
svc2 := &v1beta1.Service{Selector: map[string]string{}}
|
||||
data, err = newer.Scheme.EncodeToVersion(svc2, "v1beta1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
obj, err = newer.Scheme.Decode(data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
selector = obj.(*newer.Service).Spec.Selector
|
||||
if selector == nil || len(selector) != 0 {
|
||||
t.Errorf("unexpected selector: %#v", obj)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,9 +465,11 @@ type Service struct {
|
||||
// This service's labels.
|
||||
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize services"`
|
||||
|
||||
// This service will route traffic to pods having labels matching this selector.
|
||||
Selector map[string]string `json:"selector,omitempty" description:"label keys and values that must match in order to receive traffic for this service"`
|
||||
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" description:"set up a cloud-provider-specific load balancer on an external IP"`
|
||||
// This service will route traffic to pods having labels matching this selector. If null, no endpoints will be automatically created. If empty, all pods will be selected.
|
||||
Selector map[string]string `json:"selector" description:"label keys and values that must match in order to receive traffic for this service; if empty, all pods are selected, if not specified, endpoints must be manually specified"`
|
||||
// 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"`
|
||||
|
||||
// PublicIPs are used by external load balancers.
|
||||
PublicIPs []string `json:"publicIPs,omitempty" description:"externally visible IPs from which to select the address for the external load balancer"`
|
||||
|
||||
|
||||
@@ -16,4 +16,41 @@ limitations under the License.
|
||||
|
||||
package v1beta2_test
|
||||
|
||||
import ()
|
||||
import (
|
||||
"testing"
|
||||
|
||||
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
|
||||
)
|
||||
|
||||
func TestServiceEmptySelector(t *testing.T) {
|
||||
// Nil map should be preserved
|
||||
svc := &v1beta2.Service{Selector: nil}
|
||||
data, err := newer.Scheme.EncodeToVersion(svc, "v1beta2")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
obj, err := newer.Scheme.Decode(data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
selector := obj.(*newer.Service).Spec.Selector
|
||||
if selector != nil {
|
||||
t.Errorf("unexpected selector: %#v", obj)
|
||||
}
|
||||
|
||||
// Empty map should be preserved
|
||||
svc2 := &v1beta2.Service{Selector: map[string]string{}}
|
||||
data, err = newer.Scheme.EncodeToVersion(svc2, "v1beta2")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
obj, err = newer.Scheme.Decode(data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
selector = obj.(*newer.Service).Spec.Selector
|
||||
if selector == nil || len(selector) != 0 {
|
||||
t.Errorf("unexpected selector: %#v", obj)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,9 +430,11 @@ type Service struct {
|
||||
// This service's labels.
|
||||
Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize services"`
|
||||
|
||||
// This service will route traffic to pods having labels matching this selector.
|
||||
Selector map[string]string `json:"selector,omitempty" description:"label keys and values that must match in order to receive traffic for this service"`
|
||||
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" description:"set up a cloud-provider-specific load balancer on an external IP"`
|
||||
// This service will route traffic to pods having labels matching this selector. If null, no endpoints will be automatically created. If empty, all pods will be selected.
|
||||
Selector map[string]string `json:"selector" description:"label keys and values that must match in order to receive traffic for this service; if empty, all pods are selected, if not specified, endpoints must be manually specified"`
|
||||
// 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"`
|
||||
|
||||
// PublicIPs are used by external load balancers.
|
||||
PublicIPs []string `json:"publicIPs,omitempty" description:"externally visible IPs from which to select the address for the external load balancer"`
|
||||
|
||||
|
||||
@@ -601,8 +601,8 @@ type ServiceSpec struct {
|
||||
// Optional: Supports "TCP" and "UDP". Defaults to "TCP".
|
||||
Protocol Protocol `json:"protocol,omitempty"`
|
||||
|
||||
// This service will route traffic to pods having labels matching this selector.
|
||||
Selector map[string]string `json:"selector,omitempty"`
|
||||
// This service will route traffic to pods having labels matching this selector. If null, no endpoints will be automatically created. If empty, all pods will be selected.
|
||||
Selector map[string]string `json:"selector"`
|
||||
|
||||
// PortalIP is usually assigned by the master. If specified by the user
|
||||
// we will try to respect it or else fail the request. This field can
|
||||
|
||||
@@ -438,9 +438,12 @@ func ValidateService(service *api.Service, lister ServiceLister, ctx api.Context
|
||||
} else if !supportedPortProtocols.Has(strings.ToUpper(string(service.Spec.Protocol))) {
|
||||
allErrs = append(allErrs, errs.NewFieldNotSupported("spec.protocol", service.Spec.Protocol))
|
||||
}
|
||||
if labels.Set(service.Spec.Selector).AsSelector().Empty() {
|
||||
allErrs = append(allErrs, errs.NewFieldRequired("spec.selector", service.Spec.Selector))
|
||||
|
||||
if service.Spec.Selector != nil {
|
||||
allErrs = append(allErrs, validateLabels(service.Spec.Selector, "spec.selector")...)
|
||||
}
|
||||
allErrs = append(allErrs, validateLabels(service.Labels, "labels")...)
|
||||
|
||||
if service.Spec.CreateExternalLoadBalancer {
|
||||
services, err := lister.ListServices(ctx)
|
||||
if err != nil {
|
||||
@@ -456,8 +459,6 @@ func ValidateService(service *api.Service, lister ServiceLister, ctx api.Context
|
||||
}
|
||||
}
|
||||
}
|
||||
allErrs = append(allErrs, validateLabels(service.Labels, "labels")...)
|
||||
allErrs = append(allErrs, validateLabels(service.Spec.Selector, "selector")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
||||
@@ -710,8 +710,8 @@ func TestValidateService(t *testing.T) {
|
||||
Port: 8675,
|
||||
},
|
||||
},
|
||||
// Should fail because the selector is missing.
|
||||
numErrs: 1,
|
||||
// Should be ok because the selector is missing.
|
||||
numErrs: 0,
|
||||
},
|
||||
{
|
||||
name: "valid 1",
|
||||
@@ -824,12 +824,25 @@ func TestValidateService(t *testing.T) {
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
},
|
||||
},
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: "invalid selector",
|
||||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar"},
|
||||
},
|
||||
},
|
||||
numErrs: 2,
|
||||
numErrs: 1,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user