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.ipQueue.AddAfter(key, 1*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 (bug):
This does the right thing in the race condition case, but won't it fail in the "bad" case where the IPAddress actually is garbage that needs to be collected?

eg, if I create an ExternalName service and then manually create an IPAddress for it, won't this result in the repair controller repeatedly ignoring it forever?

Code after:
	// but we also have to check the reverse, that the IPAddress to Service relation is correct
	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