github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/network/subnet_test.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package network_test
     5  
     6  import (
     7  	"net"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/core/network"
    15  )
    16  
    17  type subnetSuite struct {
    18  	testing.IsolationSuite
    19  }
    20  
    21  var _ = gc.Suite(&subnetSuite{})
    22  
    23  func (*subnetSuite) TestFindSubnetIDsForAZ(c *gc.C) {
    24  	testCases := []struct {
    25  		name           string
    26  		zoneName       string
    27  		subnetsToZones map[network.Id][]string
    28  		expected       []network.Id
    29  		expectedErr    func(error) bool
    30  	}{
    31  		{
    32  			name:           "empty",
    33  			zoneName:       "",
    34  			subnetsToZones: make(map[network.Id][]string),
    35  			expected:       make([]network.Id, 0),
    36  			expectedErr:    errors.IsNotFound,
    37  		},
    38  		{
    39  			name:     "no match",
    40  			zoneName: "fuzz",
    41  			subnetsToZones: map[network.Id][]string{
    42  				"bar": {"foo", "baz"},
    43  			},
    44  			expected:    make([]network.Id, 0),
    45  			expectedErr: errors.IsNotFound,
    46  		},
    47  		{
    48  			name:     "match",
    49  			zoneName: "foo",
    50  			subnetsToZones: map[network.Id][]string{
    51  				"bar": {"foo", "baz"},
    52  			},
    53  			expected: []network.Id{"bar"},
    54  		},
    55  		{
    56  			name:     "multi-match",
    57  			zoneName: "foo",
    58  			subnetsToZones: map[network.Id][]string{
    59  				"bar":   {"foo", "baz"},
    60  				"other": {"aaa", "foo", "xxx"},
    61  			},
    62  			expected: []network.Id{"bar", "other"},
    63  		},
    64  		{
    65  			name:     "empty zone match",
    66  			zoneName: "",
    67  			subnetsToZones: map[network.Id][]string{
    68  				"bar":   {},
    69  				"other": {},
    70  			},
    71  			expected: []network.Id{"bar", "other"},
    72  		},
    73  	}
    74  
    75  	for i, t := range testCases {
    76  		c.Logf("test %d: %s", i, t.name)
    77  
    78  		res, err := network.FindSubnetIDsForAvailabilityZone(t.zoneName, t.subnetsToZones)
    79  		if t.expectedErr != nil {
    80  			c.Check(t.expectedErr(err), jc.IsTrue)
    81  		} else {
    82  			c.Assert(err, gc.IsNil)
    83  			c.Check(res, gc.DeepEquals, t.expected)
    84  		}
    85  	}
    86  }
    87  
    88  func (*subnetSuite) TestFilterInFanNetwork(c *gc.C) {
    89  	testCases := []struct {
    90  		name     string
    91  		subnets  []network.Id
    92  		expected []network.Id
    93  	}{
    94  		{
    95  			name:     "empty",
    96  			subnets:  make([]network.Id, 0),
    97  			expected: []network.Id(nil),
    98  		},
    99  		{
   100  			name: "no match",
   101  			subnets: []network.Id{
   102  				"aaa-bbb-ccc",
   103  				"xxx-yyy-zzz",
   104  			},
   105  			expected: []network.Id{
   106  				"aaa-bbb-ccc",
   107  				"xxx-yyy-zzz",
   108  			},
   109  		},
   110  		{
   111  			name: "match",
   112  			subnets: []network.Id{
   113  				"aaa-bbb-ccc",
   114  				"foo-INFAN-bar",
   115  				"xxx-yyy-zzz",
   116  			},
   117  			expected: []network.Id{
   118  				"aaa-bbb-ccc",
   119  				"xxx-yyy-zzz",
   120  			},
   121  		},
   122  	}
   123  
   124  	for i, t := range testCases {
   125  		c.Logf("test %d: %s", i, t.name)
   126  
   127  		res := network.FilterInFanNetwork(t.subnets)
   128  		c.Check(res, gc.DeepEquals, t.expected)
   129  	}
   130  }
   131  
   132  func (*subnetSuite) TestIsInFanNetwork(c *gc.C) {
   133  	testCases := []struct {
   134  		name     string
   135  		subnet   network.Id
   136  		expected bool
   137  	}{
   138  		{
   139  			name:     "empty",
   140  			subnet:   network.Id(""),
   141  			expected: false,
   142  		},
   143  		{
   144  			name:     "no match",
   145  			subnet:   network.Id("foo-1asd-fan-network"),
   146  			expected: false,
   147  		},
   148  		{
   149  			name:     "match",
   150  			subnet:   network.Id("foo-1asd-INFAN-network"),
   151  			expected: true,
   152  		},
   153  	}
   154  
   155  	for i, t := range testCases {
   156  		c.Logf("test %d: %s", i, t.name)
   157  
   158  		res := network.IsInFanNetwork(t.subnet)
   159  		c.Check(res, gc.Equals, t.expected)
   160  	}
   161  }
   162  
   163  func (*subnetSuite) TestSubnetInfosEquality(c *gc.C) {
   164  	s1 := network.SubnetInfos{
   165  		{ID: "1"},
   166  		{ID: "2"},
   167  	}
   168  
   169  	s2 := network.SubnetInfos{
   170  		{ID: "2"},
   171  		{ID: "1"},
   172  	}
   173  
   174  	s3 := append(s2, network.SubnetInfo{ID: "3"})
   175  
   176  	c.Check(s1.EqualTo(s2), jc.IsTrue)
   177  	c.Check(s1.EqualTo(s3), jc.IsFalse)
   178  }
   179  
   180  func (*subnetSuite) TestSubnetInfosSpaceIDs(c *gc.C) {
   181  	s := network.SubnetInfos{
   182  		{ID: "1", SpaceID: network.AlphaSpaceId},
   183  		{ID: "2", SpaceID: network.AlphaSpaceId},
   184  		{ID: "3", SpaceID: "666"},
   185  	}
   186  
   187  	c.Check(s.SpaceIDs().SortedValues(), jc.DeepEquals, []string{network.AlphaSpaceId, "666"})
   188  }
   189  
   190  func (*subnetSuite) TestSubnetInfosGetByUnderLayCIDR(c *gc.C) {
   191  	s := network.SubnetInfos{
   192  		{
   193  			ID:      "1",
   194  			FanInfo: &network.FanCIDRs{FanLocalUnderlay: "10.10.10.0/24"},
   195  		},
   196  		{
   197  			ID:      "2",
   198  			FanInfo: &network.FanCIDRs{FanLocalUnderlay: "20.20.20.0/24"},
   199  		},
   200  		{
   201  			ID:      "3",
   202  			FanInfo: &network.FanCIDRs{FanLocalUnderlay: "20.20.20.0/24"},
   203  		},
   204  	}
   205  
   206  	_, err := s.GetByUnderlayCIDR("invalid")
   207  	c.Check(err, jc.Satisfies, errors.IsNotValid)
   208  
   209  	overlays, err := s.GetByUnderlayCIDR(s[0].FanLocalUnderlay())
   210  	c.Assert(err, jc.ErrorIsNil)
   211  	c.Check(overlays, gc.DeepEquals, network.SubnetInfos{s[0]})
   212  
   213  	overlays, err = s.GetByUnderlayCIDR(s[1].FanLocalUnderlay())
   214  	c.Assert(err, jc.ErrorIsNil)
   215  	c.Check(overlays, gc.DeepEquals, network.SubnetInfos{s[1], s[2]})
   216  
   217  	overlays, err = s.GetByUnderlayCIDR("30.30.30.0/24")
   218  	c.Assert(err, jc.ErrorIsNil)
   219  	c.Check(overlays, gc.HasLen, 0)
   220  }
   221  
   222  func (*subnetSuite) TestSubnetInfosGetByCIDR(c *gc.C) {
   223  	s := network.SubnetInfos{
   224  		{ID: "1", CIDR: "10.10.10.0/25", ProviderId: "1"},
   225  		{ID: "2", CIDR: "10.10.10.0/25", ProviderId: "2"},
   226  		{ID: "4", CIDR: "20.20.20.0/25"},
   227  	}
   228  
   229  	_, err := s.GetByCIDR("invalid")
   230  	c.Check(err, jc.Satisfies, errors.IsNotValid)
   231  
   232  	subs, err := s.GetByCIDR("30.30.30.0/25")
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	c.Check(subs, gc.HasLen, 0)
   235  
   236  	subs, err = s.GetByCIDR("10.10.10.0/25")
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	c.Assert(subs.EqualTo(s[:2]), jc.IsTrue)
   239  
   240  	// Check fallback CIDR-in-CIDR matching when CIDR is carved out of a
   241  	// subnet CIDR.
   242  	subs, err = s.GetByCIDR("10.10.10.0/31")
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	c.Assert(subs.EqualTo(s[:2]), jc.IsTrue, gc.Commentf("expected input that is a subset of the subnet CIDRs to be matched to a subnet"))
   245  
   246  	// Same check as above but using a different network IP which is still
   247  	// contained within the 10.10.10.0/25 subnets from the SubnetInfos list.
   248  	subs, err = s.GetByCIDR("10.10.10.8/31")
   249  	c.Assert(err, jc.ErrorIsNil)
   250  	c.Assert(subs.EqualTo(s[:2]), jc.IsTrue, gc.Commentf("expected input that is a subset of the subnet CIDRs to be matched to a subnet"))
   251  
   252  	subs, err = s.GetByCIDR("10.10.0.0/24")
   253  	c.Assert(err, jc.ErrorIsNil)
   254  	c.Assert(subs, gc.HasLen, 0, gc.Commentf("expected input that is a superset of the subnet CIDRs not to be matched to any subnet"))
   255  }
   256  
   257  func (*subnetSuite) TestSubnetInfosGetByID(c *gc.C) {
   258  	s := network.SubnetInfos{
   259  		{ID: "1"},
   260  		{ID: "2"},
   261  		{ID: "3"},
   262  	}
   263  
   264  	c.Check(s.GetByID("1"), gc.NotNil)
   265  	c.Check(s.ContainsID("1"), jc.IsTrue)
   266  
   267  	c.Check(s.GetByID("9"), gc.IsNil)
   268  	c.Check(s.ContainsID("9"), jc.IsFalse)
   269  }
   270  
   271  func (*subnetSuite) TestSubnetInfosGetByAddress(c *gc.C) {
   272  	s := network.SubnetInfos{
   273  		{ID: "1", CIDR: "10.10.10.0/24", ProviderId: "1"},
   274  		{ID: "2", CIDR: "10.10.10.0/24", ProviderId: "2"},
   275  		{ID: "3", CIDR: "20.20.20.0/24"},
   276  	}
   277  
   278  	_, err := s.GetByAddress("invalid")
   279  	c.Check(err, jc.Satisfies, errors.IsNotValid)
   280  
   281  	subs, err := s.GetByAddress("10.10.10.5")
   282  	c.Assert(err, jc.ErrorIsNil)
   283  
   284  	// We need to check these explicitly, because the IPNets of the original
   285  	// members will now be populated, making them differ.
   286  	c.Assert(subs, gc.HasLen, 2)
   287  	c.Check(subs[0].ProviderId, gc.Equals, network.Id("1"))
   288  	c.Check(subs[1].ProviderId, gc.Equals, network.Id("2"))
   289  
   290  	subs, err = s.GetByAddress("30.30.30.5")
   291  	c.Assert(err, jc.ErrorIsNil)
   292  	c.Check(subs, gc.HasLen, 0)
   293  }
   294  
   295  func (*subnetSuite) TestSubnetInfosGetBySpaceID(c *gc.C) {
   296  	s := network.SubnetInfos{
   297  		{
   298  			ID:      "1",
   299  			CIDR:    "10.10.10.0/24",
   300  			SpaceID: "666",
   301  		},
   302  		{
   303  			ID:      "2",
   304  			CIDR:    "222.0.0.0/8",
   305  			FanInfo: &network.FanCIDRs{FanLocalUnderlay: "10.10.10.0/24"},
   306  		},
   307  		{
   308  			ID:      "3",
   309  			CIDR:    "20.20.20.0/24",
   310  			SpaceID: "999",
   311  		},
   312  		{
   313  			ID:      "4",
   314  			CIDR:    "223.0.0.0/8",
   315  			FanInfo: &network.FanCIDRs{FanLocalUnderlay: "20.20.20.0/24"},
   316  			// This is to check that we don't get duplicates when retrieving
   317  			// by the underlay CIDR.
   318  			SpaceID: "999",
   319  		},
   320  	}
   321  
   322  	subs, err := s.GetBySpaceID("666")
   323  	c.Assert(err, jc.ErrorIsNil)
   324  	c.Check(subs, gc.DeepEquals, network.SubnetInfos{
   325  		{
   326  			ID:      "1",
   327  			CIDR:    "10.10.10.0/24",
   328  			SpaceID: "666",
   329  		},
   330  		{
   331  			ID:      "2",
   332  			CIDR:    "222.0.0.0/8",
   333  			FanInfo: &network.FanCIDRs{FanLocalUnderlay: "10.10.10.0/24"},
   334  			SpaceID: "666",
   335  		},
   336  	})
   337  
   338  	subs, err = s.GetBySpaceID("999")
   339  	c.Assert(err, jc.ErrorIsNil)
   340  	c.Check(subs, gc.DeepEquals, s[2:])
   341  }
   342  
   343  func (*subnetSuite) TestSubnetInfosAllSubnetInfos(c *gc.C) {
   344  	s := network.SubnetInfos{
   345  		{ID: "1", CIDR: "10.10.10.0/24", ProviderId: "1"},
   346  		{ID: "2", CIDR: "10.10.10.0/24", ProviderId: "2"},
   347  		{ID: "3", CIDR: "20.20.20.0/24"},
   348  	}
   349  
   350  	allSubs, err := s.AllSubnetInfos()
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	c.Check(allSubs, gc.DeepEquals, s)
   353  }
   354  
   355  func (*subnetSuite) TestIPRangeForCIDR(c *gc.C) {
   356  	specs := []struct {
   357  		cidr     string
   358  		expFirst net.IP
   359  		expLast  net.IP
   360  	}{
   361  		{
   362  			cidr:     "10.20.30.0/24",
   363  			expFirst: net.ParseIP("10.20.30.0"),
   364  			expLast:  net.ParseIP("10.20.30.255"),
   365  		},
   366  		{
   367  			cidr:     "10.20.28.0/22",
   368  			expFirst: net.ParseIP("10.20.28.0"),
   369  			expLast:  net.ParseIP("10.20.31.255"),
   370  		},
   371  		{
   372  			cidr:     "10.1.2.42/29",
   373  			expFirst: net.ParseIP("10.1.2.40"),
   374  			expLast:  net.ParseIP("10.1.2.47"),
   375  		},
   376  		{
   377  			cidr:     "10.1.2.42/32",
   378  			expFirst: net.ParseIP("10.1.2.42"),
   379  			expLast:  net.ParseIP("10.1.2.42"),
   380  		},
   381  		{
   382  			cidr:     "2002::1234:abcd:ffff:c0a8:101/64",
   383  			expFirst: net.ParseIP("2002:0000:0000:1234:0000:0000:0000:0000"),
   384  			expLast:  net.ParseIP("2002::1234:ffff:ffff:ffff:ffff"),
   385  		},
   386  		{
   387  			cidr:     "2001:db8:85a3::8a2e:370:7334/128",
   388  			expFirst: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
   389  			expLast:  net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
   390  		},
   391  	}
   392  
   393  	for i, spec := range specs {
   394  		c.Logf("%d. check that range for %q is [%s, %s]", i, spec.cidr, spec.expFirst, spec.expLast)
   395  		gotFirst, gotLast, err := network.IPRangeForCIDR(spec.cidr)
   396  		c.Assert(err, jc.ErrorIsNil)
   397  		c.Assert(gotFirst.String(), gc.Equals, spec.expFirst.String())
   398  		c.Assert(gotLast.String(), gc.Equals, spec.expLast.String())
   399  	}
   400  }