...

Source file src/edge-infra.dev/pkg/f8n/ipranger/ipranger_test.go

Documentation: edge-infra.dev/pkg/f8n/ipranger

     1  package ipranger
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"google.golang.org/api/compute/v1"
     8  	"gotest.tools/v3/assert"
     9  )
    10  
    11  var testProjectID = "mock-edge-test-foreman"
    12  
    13  func TestFindOrCreateSubnet(t *testing.T) {
    14  	r := &IPRanger{subnetSvc: &mockComputeSubnetsSvc{}}
    15  
    16  	for i := 0; i < 512; i++ {
    17  		subnet, err := r.FindOrCreateSubnet(testProjectID, "region0")
    18  		assert.NilError(t, err)
    19  		assert.Assert(t, subnet != Subnet{})
    20  		assert.Assert(t, subnet.aliasRangeQuota >= rangePerRequest, "got subnet with insufficient range %s", subnet.Name)
    21  		// FindOrCreateSubnet corresponds 1:1 with cluster creation, so simulate
    22  		// what should happen in external state: two alias ranges get created
    23  		r.subnetSvc.(*mockComputeSubnetsSvc).createAliasRangesForGKEinSubnet(subnet)
    24  	}
    25  
    26  	// reload "external" state from mock service
    27  	assert.NilError(t, r.InitState(testProjectID), "failed to re-initialize state after updating mock service")
    28  	assert.Assert(t, len(r.stores[testProjectID].subnets["region0"]) == 35)
    29  	// keep this around for cheap debugging
    30  	// for _, nets := range r.stores[testProjectID].subnets {
    31  	// 	for k, v := range nets {
    32  	// 		fmt.Println(k, v.Name, v.aliasRangeQuota, v.ipRange, v.region, v.Network)
    33  	// 	}
    34  	// }
    35  }
    36  
    37  func TestInitState(t *testing.T) {
    38  	r := &IPRanger{subnetSvc: &mockComputeSubnetsSvc{}}
    39  
    40  	assert.NilError(t, r.InitState(testProjectID), "failed to initialize state")
    41  
    42  	// single region, "region0"
    43  	assert.Assert(t, len(r.stores[testProjectID].subnets) == 1)
    44  	// 3 subnets in region
    45  	assert.Assert(t, len(r.stores[testProjectID].subnets["region0"]) == 3)
    46  }
    47  
    48  func TestFindAvailableSubnet(t *testing.T) {
    49  	r := &IPRanger{subnetSvc: &mockComputeSubnetsSvc{}}
    50  	assert.NilError(t, r.InitState(testProjectID), "failed to initialize state")
    51  
    52  	// using mock state
    53  	ok, subnet := r.FindAvailableSubnet(testProjectID, "region0")
    54  	assert.Assert(t, ok, "expected to find subnet with available range space, but didnt")
    55  	assert.Assert(t, subnet != Subnet{})
    56  
    57  	// literal test states
    58  	// no ranges in any region
    59  	r.stores[testProjectID].subnets = map[string]map[string]Subnet{
    60  		"region0": {
    61  			"subnet0": {
    62  				aliasRangeQuota: 1,
    63  			},
    64  			"subnet1": {
    65  				aliasRangeQuota: 0,
    66  			},
    67  		},
    68  	}
    69  	ok, subnet = r.FindAvailableSubnet(testProjectID, "region0")
    70  	assert.Assert(t, !ok, "expected to not find subnet with available range space, but did")
    71  	assert.Assert(t, subnet == Subnet{}, "expected subnet to be empty")
    72  
    73  	// no ranges in region0, ranges in region1
    74  	r.stores[testProjectID].subnets = map[string]map[string]Subnet{
    75  		"region0": {
    76  			"subnet0": {
    77  				aliasRangeQuota: 1,
    78  			},
    79  		},
    80  		"region1": {
    81  			"subnet0": {
    82  				Name:            "subnet0",
    83  				aliasRangeQuota: 2,
    84  			},
    85  		},
    86  	}
    87  	ok, subnet = r.FindAvailableSubnet(testProjectID, "region0")
    88  	assert.Assert(t, !ok, "expected to not find subnet with available range space, but did")
    89  	assert.Assert(t, subnet == Subnet{}, "expected subnet to be empty")
    90  	ok, subnet = r.FindAvailableSubnet(testProjectID, "region1")
    91  	assert.Assert(t, ok, "expected to find subnet with available range space, but didnt")
    92  	assert.Assert(t, subnet != Subnet{})
    93  
    94  	// no ranges if no regions
    95  	r.stores[testProjectID].subnets = map[string]map[string]Subnet{}
    96  	ok, subnet = r.FindAvailableSubnet(testProjectID, "region0")
    97  	assert.Assert(t, !ok, "expected to not find subnet with available range space, but did")
    98  	assert.Assert(t, subnet == Subnet{}, "expected subnet to be empty")
    99  }
   100  
   101  func TestNextSubnetName(t *testing.T) {
   102  	r := &IPRanger{subnetSvc: &mockComputeSubnetsSvc{}}
   103  	assert.NilError(t, r.InitState(testProjectID), "failed to initialize state")
   104  
   105  	next := r.nextSubnetName(testProjectID)
   106  	assert.Assert(t, "subnet2" == next, "incorrect next subnet name, should be incrementally increasing")
   107  }
   108  
   109  func TestBuildRangeStore(t *testing.T) {
   110  	r := &IPRanger{subnetSvc: &mockComputeSubnetsSvc{}}
   111  
   112  	snlist, err := r.subnetSvc.List(testProjectID)
   113  	assert.NilError(t, err)
   114  	store, err := buildRangeStore(snlist)
   115  	assert.NilError(t, err)
   116  
   117  	// empirically observed values. ensures calculations dont drift given the same input
   118  	assert.Assert(t, len(store.ipset.Ranges()) == 1, "incorrect amount of ip ranges in built set")
   119  	c := 0
   120  	for {
   121  		_, newSet, ok := store.ipset.RemoveFreePrefix(Netmask)
   122  		if !ok {
   123  			break
   124  		}
   125  		store.ipset = newSet
   126  		c++
   127  	}
   128  	assert.Assert(t, 4086 == c, "incorrect amount of allocatable /%d ranges", Netmask)
   129  }
   130  
   131  // Mock service implementations
   132  type mockComputeSubnetsSvc struct {
   133  	fakeState computeSubnetAggregatedList
   134  }
   135  
   136  func (g *mockComputeSubnetsSvc) List(project string) (computeSubnetAggregatedList, error) {
   137  	if g.fakeState == nil {
   138  		g.fakeState = computeSubnetAggregatedList{
   139  			"regions/region0": compute.SubnetworksScopedList{
   140  				Subnetworks: []*compute.Subnetwork{
   141  					{
   142  						Name:        "subnet0",
   143  						Region:      toRegionURI(project, "region0"),
   144  						IpCidrRange: "10.0.0.0/20",
   145  						SecondaryIpRanges: []*compute.SubnetworkSecondaryRange{
   146  							{
   147  								RangeName:   "alias-range0",
   148  								IpCidrRange: "10.0.32.0/20",
   149  							},
   150  							{
   151  								RangeName:   "alias-range1",
   152  								IpCidrRange: "10.0.48.0/20",
   153  							},
   154  						},
   155  					},
   156  					{
   157  						Name:        "subnet1",
   158  						Region:      toRegionURI(project, "region0"),
   159  						IpCidrRange: "10.0.16.0/20",
   160  					},
   161  					{
   162  						Name:        "default",
   163  						Region:      toRegionURI(project, "region0"),
   164  						IpCidrRange: "10.0.64.0/20",
   165  					},
   166  				},
   167  			},
   168  		}
   169  	}
   170  	return g.fakeState, nil
   171  }
   172  
   173  func (g *mockComputeSubnetsSvc) Create(project, ipRange, region, name string) (*compute.Subnetwork, error) {
   174  	toCreate := &compute.Subnetwork{
   175  		Name:              name,
   176  		Region:            toRegionURI(project, region),
   177  		IpCidrRange:       ipRange,
   178  		SecondaryIpRanges: []*compute.SubnetworkSecondaryRange{},
   179  	}
   180  	rkey := fmt.Sprintf("regions/%s", region)
   181  	sns := g.fakeState[rkey].Subnetworks
   182  	g.fakeState[rkey] = compute.SubnetworksScopedList{
   183  		Subnetworks: append(sns, toCreate),
   184  	}
   185  	return toCreate, nil
   186  }
   187  
   188  func (g *mockComputeSubnetsSvc) createAliasRangesForGKEinSubnet(subnet Subnet) {
   189  	rkey := fmt.Sprintf("regions/%s", subnet.region)
   190  	for _, s := range g.fakeState[rkey].Subnetworks {
   191  		if s.Name == subnet.Name {
   192  			s.SecondaryIpRanges = append(s.SecondaryIpRanges, &compute.SubnetworkSecondaryRange{
   193  				RangeName:   "fake-gkeABC-pods-range",
   194  				IpCidrRange: "10.0.0.0/21",
   195  			})
   196  			s.SecondaryIpRanges = append(s.SecondaryIpRanges, &compute.SubnetworkSecondaryRange{
   197  				RangeName:   "fake-gkeABC-services-range",
   198  				IpCidrRange: "10.0.8.0/21",
   199  			})
   200  		}
   201  	}
   202  }
   203  

View as plain text