func (rw blockReaderWriter) getPendingAffinity(ctx context.Context, host string, subnet cnet.IPNet) (*model.KVPair, error) {
	logCtx := log.WithFields(log.Fields{"host": host, "subnet": subnet})
	logCtx.Info("Trying to create affinity in pending state")
	obj := model.KVPair{
		Key:   model.BlockAffinityKey{Host: host, CIDR: subnet},
		Value: &model.BlockAffinity{State: model.StatePending},
	}
	aff, err := rw.client.Create(ctx, &obj)
	if err != nil {
		if _, ok := err.(cerrors.ErrorResourceAlreadyExists); !ok {
			logCtx.WithError(err).Error("Failed to claim affinity")
			return nil, err
		}
		logCtx.Info("Block affinity already exists, getting existing affinity")
		aff, err = rw.queryAffinity(ctx, host, subnet, "")
		if err != nil {
			logCtx.WithError(err).Error("Failed to get existing affinity")
			return nil, err
		}
		logCtx.Info("Got existing affinity")
		if aff.Value.(*model.BlockAffinity).State != model.StateConfirmed {
			logCtx.Infof("Marking existing affinity with current state %s as pending", aff.Value.(*model.BlockAffinity).State)
			aff.Value.(*model.BlockAffinity).State = model.StatePending
			return rw.updateAffinity(ctx, aff)
		}
		logCtx.Info("Existing affinity is already confirmed")
		return aff, nil
	}
	logCtx.Infof("Successfully created pending affinity for block")
	return aff, nil
}
getPendingAffinity claims a pending affinity for the given host and subnet. The affinity can then
be used to claim a block. If an affinity already exists, it will return that affinity.
package ipam
import (
	"context"
	"errors"
	"fmt"
	"hash/fnv"
	"math/big"
	"math/rand"
	"net"
	"time"
	log "github.com/sirupsen/logrus"
	v3 "github.com/projectcalico/api/pkg/apis/projectcalico/v3"
	bapi "github.com/projectcalico/libcalico-go/lib/backend/api"
	"github.com/projectcalico/libcalico-go/lib/backend/model"
	cerrors "github.com/projectcalico/libcalico-go/lib/errors"
	cnet "github.com/projectcalico/libcalico-go/lib/net"
)
type blockReaderWriter struct {
	client bapi.Client
	pools  PoolAccessorInterface
}
func (rw blockReaderWriter) getAffineBlocks(
	ctx context.Context,
	host string,
	ver int,
) (
	blocks []cnet.IPNet,
	err error,
) {
	opts := model.BlockAffinityListOptions{Host: host, IPVersion: ver}
	datastoreObjs, err := rw.client.List(ctx, opts, "")
	if err != nil {
		if _, ok := err.(cerrors.ErrorResourceDoesNotExist); ok {
			return
		} else {
			log.Errorf("Error getting affine blocks: %v", err)
			return
		}
	}
	for _, o := range datastoreObjs.KVPairs {
		k := o.Key.(model.BlockAffinityKey)
		blocks = append(blocks, k.CIDR)
	}
	return
}
func filterBlocksByPools(blocks []cnet.IPNet, pools []v3.IPPool) (blocksInPools, blocksNotInPools []cnet.IPNet, err error) {
	for _, block := range blocks {
		var pool *v3.IPPool
		pool, err = findContainingPool(pools, block.IP)
		if err != nil {
			return
		}
		if pool != nil {
			blocksInPools = append(blocksInPools, block)
		} else {
			blocksNotInPools = append(blocksNotInPools, block)
		}
	}
	return
}
func findContainingPool(pools []v3.IPPool, addr net.IP) (*v3.IPPool, error) {
	for _, pool := range pools {
		var poolNet *cnet.IPNet
		_, poolNet, err := cnet.ParseCIDR(pool.Spec.CIDR)
		if err != nil {
			log.Errorf("Error parsing CIDR: %s from pool: %s %v", pool.Spec.CIDR, pool.Name, err)
			return nil, err
		}
		if poolNet.Contains(addr) {
			return &pool, nil
		}
	}
	return nil, nil
}
func (rw blockReaderWriter) findUsableBlock(ctx context.Context, host string, version int, pools []v3.IPPool, reservations addrFilter, config IPAMConfig) (*cnet.IPNet, error) {
	if len(pools) == 0 {
		return nil, fmt.Errorf("no configured Calico pools for node %s", host)
	}
	existingBlocks, err := rw.listBlocks(ctx, "")
	if err != nil {
		return nil, err
	}
	type blockInfo struct {
		numFree  int
		affinity string
	}
	exists := map[string]blockInfo{}
	for _, e := range existingBlocks.KVPairs {
		hostAff := e.Value.(*model.AllocationBlock).Host()
		numFree := allocationBlock{e.Value.(*model.AllocationBlock)}.NumFreeAddresses(reservations)
		exists[e.Key.(model.BlockKey).CIDR.String()] = blockInfo{numFree: numFree, affinity: hostAff}
	}
	for _, pool := range pools {
		log.Debugf("Looking for blocks in pool %+v", pool)
		blocks := randomBlockGenerator(pool, host)
		for subnet := blocks(); subnet != nil; subnet = blocks() {
			if reservations.MatchesWholeCIDR(subnet) {
				log.WithField("cidr", subnet).Debug("Skipping block that is entirely reserved.")
				continue
			}
			log.Debugf("Getting block: %s", subnet.String())
			if info, ok := exists[subnet.String()]; !ok {
				log.Infof("Found free block: %+v", *subnet)
				return subnet, nil
			} else if info.affinity == host && info.numFree != 0 {
				log.Debugf("Block %s already assigned to host, has free space", subnet.String())
				return subnet, nil
			}
			log.Debugf("Block %s already exists and is either affine to another host or has no space, try another", subnet.String())
		}
	}
	return nil, noFreeBlocksError("No Free Blocks")
}