Repository: kubernetes/kubernetes
File: pkg/registry/core/service/ipallocator/controller/repairip.go (Go)

Code before:
	}

	// Service storage implements transactions. It creates an IPAddress object first and then creates
	// the Service object, and if the Service object already exists the complete transaction is
	// reverted. There can be race conditions when the repair loop picks up the new IPAddress object
	// for reconciliation before the transaction is reverted. This leads to spurious
	// IPAddressWrongReference warnings, to suppress these warnings we delay the processing of the new
	// IPAddress object by 5 seconds. The service allocation creates the IPAddress object before creating
	// the Service object, we easily identify this scenario when the IPAddress object creation timestamp
	// is after the Service creation timestamp. We do this only when the IPAddress object is created
	// recently in order to avoid indefinitely requeue/delay in IPAddress cleanup if for some reason
	// the service transaction revert fails.
	if ipAddress.CreationTimestamp.After(svc.CreationTimestamp.Time) &&
		r.clock.Now().Sub(ipAddress.CreationTimestamp.Time) < 5*time.Second {
		// requeue after the grace period
		r.ipQueue.AddAfter(key, 5*time.Second)
		return nil
	}

	// The service is of type ExternalName and the IPAddress was created after the Service,
	// this can happen if the Service has been mutated from ExternalName to ClusterIP, so an
	// IPAddress is allocated after the mutation but the repair informer has not been updated yet.
	if svc.Spec.Type == v1.ServiceTypeExternalName &&
		ipAddress.CreationTimestamp.Time.After(svc.CreationTimestamp.Time) &&
		r.clock.Now().Sub(ipAddress.CreationTimestamp.Time) < 5*time.Second {
		r.ipQueue.AddAfter(key, 5*time.Second)
		return nil
	}
	runtime.HandleError(fmt.Errorf("the IPAddress: %s for Service %s/%s has a wrong reference %#v; cleaning up", ipAddress.Name, svc.Name, svc.Namespace, ipAddress.Spec.ParentRef))
	r.recorder.Eventf(ipAddress, nil, v1.EventTypeWarning, "IPAddressWrongReference", "IPAddressAllocation", "IPAddress: %s for Service %s/%s has a wrong reference; cleaning up", ipAddress.Name, svc.Namespace, svc.Name)
	err = r.client.NetworkingV1().IPAddresses().Delete(context.Background(), ipAddress.Name, metav1.DeleteOptions{})
	if err != nil && !apierrors.IsNotFound(err) {
		return err
	}
	return nil

}

// isIPOutOfRange returns true if the IP is not contained in any of the ServiceCIDRs
func (r *RepairIPAddress) isIPOutOfRange(ip net.IP) bool {
	return len(servicecidr.ContainsIP(r.serviceCIDRLister, ip)) == 0
}

func newIPAddress(name string, svc *v1.Service) *networkingv1.IPAddress {
	family := string(v1.IPv4Protocol)
	if netutils.IsIPv6String(name) {
		family = string(v1.IPv6Protocol)
	}
	return &networkingv1.IPAddress{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,

Review comment (performance):
Blah. There's no nicer way to handle that? (Oh, though I see it's similar to what we do in other cases...)

Actually... are we sure this is the only informer cache consistency problem? It seems like a lot of the checks depend on the caches being in sync...

Code after:
	for _, clusterIP := range svc.Spec.ClusterIPs {
		if ipAddress.Name == clusterIP {
			return nil
		}
	}

	// There is a special case when we "break the immutability" of the Service ClusterIPs,
	// and we allocate IP addresses on Update. This is when a Service is mutated from Type ExternalName
	// to any of the Types that require ClusterIP creation. Similar to the same logic we use to handle
	// the case when the Service is created after the IPAddress, we delay the processing of the new
	// IPAddress object by the 60 seconds timeout of the kube-apiserver.
	if svc.Spec.Type == v1.ServiceTypeExternalName {
		ipLifetime := r.clock.Now().Sub(ipAddress.CreationTimestamp.Time)
		gracePeriod := 60 * time.Second
		if ipLifetime < gracePeriod {
			// requeue after the grace period
			r.ipQueue.AddAfter(key, gracePeriod-ipLifetime)
			return nil
		}
	}

	// Service storage implements transactions. It creates an IPAddress object first and then creates
	// the Service object, and if the Service object already exists the complete transaction is
	// reverted. There can be race conditions when the repair loop picks up the new IPAddress object
	// for reconciliation before the transaction is reverted. This leads to spurious
	// IPAddressWrongReference warnings, to suppress these warnings we delay the processing of the new
	// IPAddress object by 5 seconds. The service allocation creates the IPAddress object before creating
	// the Service object, we easily identify this scenario when the IPAddress object creation timestamp
	// is after the Service creation timestamp. We do this only when the IPAddress object is created
	// recently in order to avoid indefinitely requeue/delay in IPAddress cleanup if for some reason
	// the service transaction revert fails.
	if ipAddress.CreationTimestamp.After(svc.CreationTimestamp.Time) &&
		r.clock.Now().Sub(ipAddress.CreationTimestamp.Time) < 5*time.Second {
		// requeue after the grace period
		r.ipQueue.AddAfter(key, 5*time.Second)
		return nil
	}
	runtime.HandleError(fmt.Errorf("the IPAddress: %s for Service %s/%s has a wrong reference %#v; cleaning up", ipAddress.Name, svc.Name, svc.Namespace, ipAddress.Spec.ParentRef))
	r.recorder.Eventf(ipAddress, nil, v1.EventTypeWarning, "IPAddressWrongReference", "IPAddressAllocation", "IPAddress: %s for Service %s/%s has a wrong reference; cleaning up", ipAddress.Name, svc.Namespace, svc.Name)
	err = r.client.NetworkingV1().IPAddresses().Delete(context.Background(), ipAddress.Name, metav1.DeleteOptions{})
	if err != nil && !apierrors.IsNotFound(err) {
		return err
	}
	return nil

}

// isIPOutOfRange returns true if the IP is not contained in any of the ServiceCIDRs
func (r *RepairIPAddress) isIPOutOfRange(ip net.IP) bool {
	return len(servicecidr.ContainsIP(r.serviceCIDRLister, ip)) == 0
}