mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 07:47:56 +00:00
Merge pull request #31311 from girishkalele/nodename_validation
Automatic merge from submit-queue Enforce EndpointAddress.NodeName validation Fixes https://github.com/kubernetes/kubernetes/issues/30646 - disallows user setting upon create or modifying during update.
This commit is contained in:
commit
f673bc8840
@ -3358,17 +3358,50 @@ func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *api.Namespace)
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Construct lookup map of old subset IPs to NodeNames.
|
||||||
|
func updateEpAddrToNodeNameMap(ipToNodeName map[string]string, addresses []api.EndpointAddress) {
|
||||||
|
for n := range addresses {
|
||||||
|
if addresses[n].NodeName == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ipToNodeName[addresses[n].IP] = *addresses[n].NodeName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a map across all subsets of IP -> NodeName
|
||||||
|
func buildEndpointAddressNodeNameMap(subsets []api.EndpointSubset) map[string]string {
|
||||||
|
ipToNodeName := make(map[string]string)
|
||||||
|
for i := range subsets {
|
||||||
|
updateEpAddrToNodeNameMap(ipToNodeName, subsets[i].Addresses)
|
||||||
|
updateEpAddrToNodeNameMap(ipToNodeName, subsets[i].NotReadyAddresses)
|
||||||
|
}
|
||||||
|
return ipToNodeName
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateEpAddrNodeNameTransition(addr *api.EndpointAddress, ipToNodeName map[string]string, fldPath *field.Path) field.ErrorList {
|
||||||
|
errList := field.ErrorList{}
|
||||||
|
existingNodeName, found := ipToNodeName[addr.IP]
|
||||||
|
if !found {
|
||||||
|
return errList
|
||||||
|
}
|
||||||
|
if addr.NodeName == nil || *addr.NodeName == existingNodeName {
|
||||||
|
return errList
|
||||||
|
}
|
||||||
|
// NodeName entry found for this endpoint IP, but user is attempting to change NodeName
|
||||||
|
return append(errList, field.Forbidden(fldPath, fmt.Sprintf("Cannot change NodeName for %s to %s", addr.IP, *addr.NodeName)))
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateEndpoints tests if required fields are set.
|
// ValidateEndpoints tests if required fields are set.
|
||||||
func ValidateEndpoints(endpoints *api.Endpoints) field.ErrorList {
|
func ValidateEndpoints(endpoints *api.Endpoints) field.ErrorList {
|
||||||
allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata"))
|
allErrs := ValidateObjectMeta(&endpoints.ObjectMeta, true, ValidateEndpointsName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...)
|
allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(endpoints.Annotations, field.NewPath("annotations"))...)
|
||||||
allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, field.NewPath("subsets"))...)
|
allErrs = append(allErrs, validateEndpointSubsets(endpoints.Subsets, []api.EndpointSubset{}, field.NewPath("subsets"))...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateEndpointSubsets(subsets []api.EndpointSubset, fldPath *field.Path) field.ErrorList {
|
func validateEndpointSubsets(subsets []api.EndpointSubset, oldSubsets []api.EndpointSubset, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
ipToNodeName := buildEndpointAddressNodeNameMap(oldSubsets)
|
||||||
for i := range subsets {
|
for i := range subsets {
|
||||||
ss := &subsets[i]
|
ss := &subsets[i]
|
||||||
idxPath := fldPath.Index(i)
|
idxPath := fldPath.Index(i)
|
||||||
@ -3381,10 +3414,10 @@ func validateEndpointSubsets(subsets []api.EndpointSubset, fldPath *field.Path)
|
|||||||
allErrs = append(allErrs, field.Required(idxPath.Child("ports"), ""))
|
allErrs = append(allErrs, field.Required(idxPath.Child("ports"), ""))
|
||||||
}
|
}
|
||||||
for addr := range ss.Addresses {
|
for addr := range ss.Addresses {
|
||||||
allErrs = append(allErrs, validateEndpointAddress(&ss.Addresses[addr], idxPath.Child("addresses").Index(addr))...)
|
allErrs = append(allErrs, validateEndpointAddress(&ss.Addresses[addr], idxPath.Child("addresses").Index(addr), ipToNodeName)...)
|
||||||
}
|
}
|
||||||
for addr := range ss.NotReadyAddresses {
|
for addr := range ss.NotReadyAddresses {
|
||||||
allErrs = append(allErrs, validateEndpointAddress(&ss.NotReadyAddresses[addr], idxPath.Child("notReadyAddresses").Index(addr))...)
|
allErrs = append(allErrs, validateEndpointAddress(&ss.NotReadyAddresses[addr], idxPath.Child("notReadyAddresses").Index(addr), ipToNodeName)...)
|
||||||
}
|
}
|
||||||
for port := range ss.Ports {
|
for port := range ss.Ports {
|
||||||
allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, idxPath.Child("ports").Index(port))...)
|
allErrs = append(allErrs, validateEndpointPort(&ss.Ports[port], len(ss.Ports) > 1, idxPath.Child("ports").Index(port))...)
|
||||||
@ -3394,7 +3427,7 @@ func validateEndpointSubsets(subsets []api.EndpointSubset, fldPath *field.Path)
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateEndpointAddress(address *api.EndpointAddress, fldPath *field.Path) field.ErrorList {
|
func validateEndpointAddress(address *api.EndpointAddress, fldPath *field.Path, ipToNodeName map[string]string) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
for _, msg := range validation.IsValidIP(address.IP) {
|
for _, msg := range validation.IsValidIP(address.IP) {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("ip"), address.IP, msg))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("ip"), address.IP, msg))
|
||||||
@ -3402,6 +3435,11 @@ func validateEndpointAddress(address *api.EndpointAddress, fldPath *field.Path)
|
|||||||
if len(address.Hostname) > 0 {
|
if len(address.Hostname) > 0 {
|
||||||
allErrs = append(allErrs, ValidateDNS1123Label(address.Hostname, fldPath.Child("hostname"))...)
|
allErrs = append(allErrs, ValidateDNS1123Label(address.Hostname, fldPath.Child("hostname"))...)
|
||||||
}
|
}
|
||||||
|
// During endpoint update, validate NodeName is DNS1123 compliant and transition rules allow the update
|
||||||
|
if address.NodeName != nil {
|
||||||
|
allErrs = append(allErrs, ValidateDNS1123Label(*address.NodeName, fldPath.Child("nodeName"))...)
|
||||||
|
}
|
||||||
|
allErrs = append(allErrs, validateEpAddrNodeNameTransition(address, ipToNodeName, fldPath.Child("nodeName"))...)
|
||||||
if len(allErrs) > 0 {
|
if len(allErrs) > 0 {
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
@ -3456,7 +3494,7 @@ func validateEndpointPort(port *api.EndpointPort, requireName bool, fldPath *fie
|
|||||||
// ValidateEndpointsUpdate tests to make sure an endpoints update can be applied.
|
// ValidateEndpointsUpdate tests to make sure an endpoints update can be applied.
|
||||||
func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *api.Endpoints) field.ErrorList {
|
func ValidateEndpointsUpdate(newEndpoints, oldEndpoints *api.Endpoints) field.ErrorList {
|
||||||
allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
|
allErrs := ValidateObjectMetaUpdate(&newEndpoints.ObjectMeta, &oldEndpoints.ObjectMeta, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, validateEndpointSubsets(newEndpoints.Subsets, field.NewPath("subsets"))...)
|
allErrs = append(allErrs, validateEndpointSubsets(newEndpoints.Subsets, oldEndpoints.Subsets, field.NewPath("subsets"))...)
|
||||||
allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(newEndpoints.Annotations, field.NewPath("annotations"))...)
|
allErrs = append(allErrs, ValidateEndpointsSpecificAnnotations(newEndpoints.Annotations, field.NewPath("annotations"))...)
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
@ -7992,3 +7992,42 @@ func TestValidateSysctls(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newNodeNameEndpoint(nodeName string) *api.Endpoints {
|
||||||
|
ep := &api.Endpoints{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
ResourceVersion: "1",
|
||||||
|
},
|
||||||
|
Subsets: []api.EndpointSubset{
|
||||||
|
{
|
||||||
|
NotReadyAddresses: []api.EndpointAddress{},
|
||||||
|
Ports: []api.EndpointPort{{Name: "https", Port: 443, Protocol: "TCP"}},
|
||||||
|
Addresses: []api.EndpointAddress{
|
||||||
|
{
|
||||||
|
IP: "8.8.8.8",
|
||||||
|
Hostname: "zookeeper1",
|
||||||
|
NodeName: &nodeName}}}}}
|
||||||
|
return ep
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEndpointAddressNodeNameUpdateRestrictions(t *testing.T) {
|
||||||
|
oldEndpoint := newNodeNameEndpoint("kubernetes-minion-setup-by-backend")
|
||||||
|
updatedEndpoint := newNodeNameEndpoint("kubernetes-changed-nodename")
|
||||||
|
// Check that NodeName cannot be changed during update (if already set)
|
||||||
|
errList := ValidateEndpoints(updatedEndpoint)
|
||||||
|
errList = append(errList, ValidateEndpointsUpdate(updatedEndpoint, oldEndpoint)...)
|
||||||
|
if len(errList) == 0 {
|
||||||
|
t.Error("Endpoint should not allow changing of Subset.Addresses.NodeName on update")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEndpointAddressNodeNameInvalidDNS1123(t *testing.T) {
|
||||||
|
// Check NodeName DNS validation
|
||||||
|
endpoint := newNodeNameEndpoint("illegal.nodename")
|
||||||
|
errList := ValidateEndpoints(endpoint)
|
||||||
|
if len(errList) == 0 {
|
||||||
|
t.Error("Endpoint should reject invalid NodeName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user