Change:
What happened: When creating a headless service locally with set as , field has to be specified. Otherwise, endpoint controller will not be able to create an endpoint for the headless service successfully. This is because port number of to-be-created endpoint is defaulted to 0 when the field is not specified, which results in an error (0 is a reserved number not available for TCP and UDP usage). This explicitly suggests that the field is optional, which can be confusing to those unfamiliar with kubernetes networking. What you expected to happen: Port number of to-be-created endpoint should be defaulted to any value from 1 to , not 0. Apart from this, the tutorial should not suggest the field as optional until the defaulting is fixed. How to reproduce it (as minimally and precisely as possible): : an to refer, but remember to specify field into controller manager log and search for errors related to endpoint creation for the headless service Environment: Kubernetes version (use ):Cloud provider or hardware configuration: local cluster OS (e.g. from /etc/os-release): /cc
In endpoints controller we have explicit logic allowing headless service not to have ports: Though confirmed creating a headless service with no ports defined hit the exact same error: Will investigate more to see if this is a recent regression on endpoint API validation.
Ref , which reported the same bug.
Thanks for looking at the issue! Tell me if I can be of any help. :)
Sure thing :) I have a fix opened . /assign

Discussion:
// Allow headless service not to have ports. if len(service.Spec.Ports) == 0 { if service.Spec.ClusterIP == api.ClusterIPNone { <del> epp := v1.EndpointPort{Port: 0, Protocol: v1.ProtocolTCP} subsets, totalReadyEps, totalNotReadyEps = addEndpointSubset(subsets, pod, epa, epp, tolerateUnreadyEndpoints) </del> <ins> subsets, totalReadyEps, totalNotReadyEps = addEndpointSubset(subsets, pod, epa, nil, tolerateUnreadyEndpoints) // No need to repack subsets for headless service without ports. </ins> } } else { for i := range service.Spec.Ports {

} var readyEps, notReadyEps int <del> epp := v1.EndpointPort{Name: portName, Port: int32(portNum), Protocol: portProto} </del> <ins> epp := &v1.EndpointPort{Name: portName, Port: int32(portNum), Protocol: portProto} </ins> subsets, readyEps, notReadyEps = addEndpointSubset(subsets, pod, epa, epp, tolerateUnreadyEndpoints) totalReadyEps = totalReadyEps + readyEps totalNotReadyEps = totalNotReadyEps + notReadyEps } <ins> subsets = endpoints.RepackSubsets(subsets) </ins> } } <del> subsets = endpoints.RepackSubsets(subsets) </del> // See if there's actually an update here. currentEndpoints, err := e.endpointsLister.Endpoints(service.Namespace).Get(service.Name)

} func addEndpointSubset(subsets []v1.EndpointSubset, pod *v1.Pod, epa v1.EndpointAddress, <del> epp v1.EndpointPort, tolerateUnreadyEndpoints bool) ([]v1.EndpointSubset, int, int) { </del> <ins> epp *v1.EndpointPort, tolerateUnreadyEndpoints bool) ([]v1.EndpointSubset, int, int) { </ins> var readyEps int = 0 var notReadyEps int = 0 <ins> ports := []v1.EndpointPort{} if epp != nil { ports = append(ports, *epp) } </ins> if tolerateUnreadyEndpoints || podutil.IsPodReady(pod) { subsets = append(subsets, v1.EndpointSubset{ Addresses: []v1.EndpointAddress{epa}, <del> Ports:     []v1.EndpointPort{epp}, </del> <ins> Ports:     ports, </ins> }) readyEps++ } else if shouldPodBeInEndpoints(pod) { glog.V(5).Infof("Pod is out of service: %s/%s", pod.Namespace, pod.Name) subsets = append(subsets, v1.EndpointSubset{ NotReadyAddresses: []v1.EndpointAddress{epa}, <del> Ports:             []v1.EndpointPort{epp}, </del> <ins> Ports:             ports, </ins> }) notReadyEps++ }

}, Subsets: []v1.EndpointSubset{{ Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}}, <del> Ports:     []v1.EndpointPort{{Port: 0, Protocol: "TCP"}}, </del> <ins> Ports:     []v1.EndpointPort{}, </ins> }}, }) endpointsHandler.ValidateRequestCount(t, 1)