github.com/cilium/cilium@v1.16.2/pkg/ipam/crd_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package ipam
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"net"
    10  	"net/netip"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	fakeTypes "github.com/cilium/cilium/pkg/datapath/fake/types"
    18  	ipamOption "github.com/cilium/cilium/pkg/ipam/option"
    19  	ipamTypes "github.com/cilium/cilium/pkg/ipam/types"
    20  	"github.com/cilium/cilium/pkg/lock"
    21  	"github.com/cilium/cilium/pkg/node"
    22  	"github.com/cilium/cilium/pkg/option"
    23  	"github.com/cilium/cilium/pkg/trigger"
    24  )
    25  
    26  func TestIPNotAvailableInPoolError(t *testing.T) {
    27  	err := NewIPNotAvailableInPoolError(net.ParseIP("1.1.1.1"))
    28  	err2 := NewIPNotAvailableInPoolError(net.ParseIP("1.1.1.1"))
    29  	assert.Equal(t, err, err2)
    30  	assert.True(t, errors.Is(err, err2))
    31  
    32  	err = NewIPNotAvailableInPoolError(net.ParseIP("2.1.1.1"))
    33  	err2 = NewIPNotAvailableInPoolError(net.ParseIP("1.1.1.1"))
    34  	assert.NotEqual(t, err, err2)
    35  	assert.False(t, errors.Is(err, err2))
    36  
    37  	err = NewIPNotAvailableInPoolError(net.ParseIP("2.1.1.1"))
    38  	err2 = errors.New("another error")
    39  	assert.NotEqual(t, err, err2)
    40  	assert.False(t, errors.Is(err, err2))
    41  
    42  	err = errors.New("another error")
    43  	err2 = NewIPNotAvailableInPoolError(net.ParseIP("2.1.1.1"))
    44  	assert.NotEqual(t, err, err2)
    45  	assert.False(t, errors.Is(err, err2))
    46  
    47  	err = NewIPNotAvailableInPoolError(net.ParseIP("1.1.1.1"))
    48  	err2 = nil
    49  	assert.False(t, errors.Is(err, err2))
    50  
    51  	err = nil
    52  	err2 = NewIPNotAvailableInPoolError(net.ParseIP("1.1.1.1"))
    53  	assert.False(t, errors.Is(err, err2))
    54  
    55  	// We don't match against strings. It must be the sentinel value.
    56  	err = errors.New("IP 2.1.1.1 is not available")
    57  	err2 = NewIPNotAvailableInPoolError(net.ParseIP("2.1.1.1"))
    58  	assert.NotEqual(t, err, err2)
    59  	assert.False(t, errors.Is(err, err2))
    60  }
    61  
    62  var testConfigurationCRD = &option.DaemonConfig{
    63  	ConfigPatchMutex:        new(lock.RWMutex),
    64  	EnableIPv4:              true,
    65  	EnableIPv6:              false,
    66  	EnableHealthChecking:    true,
    67  	EnableUnreachableRoutes: false,
    68  	IPAM:                    ipamOption.IPAMCRD,
    69  }
    70  
    71  func newFakeNodeStore(conf *option.DaemonConfig, t *testing.T) *nodeStore {
    72  	tr, err := trigger.NewTrigger(trigger.Parameters{
    73  		Name:        "fake-crd-allocator-node-refresher",
    74  		MinInterval: 3 * time.Second,
    75  		TriggerFunc: func(reasons []string) {},
    76  	})
    77  	if err != nil {
    78  		log.WithError(err).Fatal("Unable to initialize CiliumNode synchronization trigger")
    79  	}
    80  	store := &nodeStore{
    81  		allocators:         []*crdAllocator{},
    82  		allocationPoolSize: map[Family]int{},
    83  		conf:               conf,
    84  		refreshTrigger:     tr,
    85  	}
    86  	return store
    87  }
    88  
    89  func TestMarkForReleaseNoAllocate(t *testing.T) {
    90  	cn := newCiliumNode("node1", 4, 4, 0)
    91  	dummyResource := ipamTypes.AllocationIP{Resource: "foo"}
    92  	for i := 1; i <= 4; i++ {
    93  		cn.Spec.IPAM.Pool[fmt.Sprintf("1.1.1.%d", i)] = dummyResource
    94  	}
    95  
    96  	fakeAddressing := fakeTypes.NewNodeAddressing()
    97  	conf := testConfigurationCRD
    98  	initNodeStore.Do(func() {
    99  		sharedNodeStore = newFakeNodeStore(conf, t)
   100  		sharedNodeStore.ownNode = cn
   101  	})
   102  	localNodeStore := node.NewTestLocalNodeStore(node.LocalNode{})
   103  	ipam := NewIPAM(fakeAddressing, conf, &ownerMock{}, localNodeStore, &ownerMock{}, &resourceMock{}, &mtuMock, nil, nil)
   104  	ipam.ConfigureAllocator()
   105  	sharedNodeStore.updateLocalNodeResource(cn)
   106  
   107  	// Allocate the first 3 IPs
   108  	for i := 1; i <= 3; i++ {
   109  		epipv4 := netip.MustParseAddr(fmt.Sprintf("1.1.1.%d", i))
   110  		_, err := ipam.IPv4Allocator.Allocate(epipv4.AsSlice(), fmt.Sprintf("test%d", i), PoolDefault())
   111  		require.Nil(t, err)
   112  	}
   113  
   114  	// Update 1.1.1.4 as marked for release like operator would.
   115  	cn.Status.IPAM.ReleaseIPs["1.1.1.4"] = ipamOption.IPAMMarkForRelease
   116  	// Attempts to allocate 1.1.1.4 should fail, since it's already marked for release
   117  	epipv4 := netip.MustParseAddr("1.1.1.4")
   118  	_, err := ipam.IPv4Allocator.Allocate(epipv4.AsSlice(), "test", PoolDefault())
   119  	require.Error(t, err)
   120  	// Call agent's CRD update function. status for 1.1.1.4 should change from marked for release to ready for release
   121  	sharedNodeStore.updateLocalNodeResource(cn)
   122  	require.Equal(t, ipamOption.IPAMReadyForRelease, string(cn.Status.IPAM.ReleaseIPs["1.1.1.4"]))
   123  
   124  	// Verify that 1.1.1.3 is denied for release, since it's already in use
   125  	cn.Status.IPAM.ReleaseIPs["1.1.1.3"] = ipamOption.IPAMMarkForRelease
   126  	sharedNodeStore.updateLocalNodeResource(cn)
   127  	require.Equal(t, ipamOption.IPAMDoNotRelease, string(cn.Status.IPAM.ReleaseIPs["1.1.1.3"]))
   128  }