github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/firewall/egressaddresswatcher_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package firewall_test
     5  
     6  import (
     7  	"github.com/juju/names/v5"
     8  	jc "github.com/juju/testing/checkers"
     9  	gc "gopkg.in/check.v1"
    10  
    11  	"github.com/juju/juju/apiserver/common"
    12  	"github.com/juju/juju/apiserver/common/firewall"
    13  	apiservertesting "github.com/juju/juju/apiserver/testing"
    14  	"github.com/juju/juju/core/network"
    15  	"github.com/juju/juju/core/watcher"
    16  	statetesting "github.com/juju/juju/state/testing"
    17  	coretesting "github.com/juju/juju/testing"
    18  )
    19  
    20  var _ = gc.Suite(&addressWatcherSuite{})
    21  
    22  type addressWatcherSuite struct {
    23  	coretesting.BaseSuite
    24  
    25  	resources  *common.Resources
    26  	authorizer *apiservertesting.FakeAuthorizer
    27  	st         *mockState
    28  }
    29  
    30  func (s *addressWatcherSuite) SetUpTest(c *gc.C) {
    31  	s.BaseSuite.SetUpTest(c)
    32  
    33  	s.resources = common.NewResources()
    34  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
    35  
    36  	s.authorizer = &apiservertesting.FakeAuthorizer{
    37  		Tag:        names.NewMachineTag("0"),
    38  		Controller: true,
    39  	}
    40  
    41  	s.st = newMockState(coretesting.ModelTag.Id())
    42  }
    43  
    44  func (s *addressWatcherSuite) setupRelation(c *gc.C, addr string) *mockRelation {
    45  	rel := newMockRelation(123)
    46  	rel.ruwApp = "django"
    47  	// Initial event.
    48  	rel.ew.changes <- []string{}
    49  	s.st.relations["remote-db2:db django:db"] = rel
    50  	unit := newMockUnit("django/0")
    51  	unit.publicAddress = network.NewSpaceAddress(addr)
    52  	unit.machineId = "0"
    53  	s.st.units["django/0"] = unit
    54  	app := newMockApplication("django")
    55  	app.units = []*mockUnit{unit}
    56  	s.st.applications["django"] = app
    57  	s.st.machines["0"] = newMockMachine("0")
    58  	return rel
    59  }
    60  
    61  func (s *addressWatcherSuite) TestInitial(c *gc.C) {
    62  	rel := s.setupRelation(c, "54.1.2.3")
    63  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
    64  	c.Assert(err, jc.ErrorIsNil)
    65  	defer statetesting.AssertStop(c, w)
    66  	wc := statetesting.NewStringsWatcherC(c, w)
    67  	// django/0 is initially in scope
    68  	rel.ruw.changes <- watcher.RelationUnitsChange{
    69  		Changed: map[string]watcher.UnitSettings{
    70  			"django/0": {},
    71  		},
    72  	}
    73  
    74  	wc.AssertChange("54.1.2.3/32")
    75  	wc.AssertNoChange()
    76  }
    77  
    78  func (s *addressWatcherSuite) TestUnitEntersScope(c *gc.C) {
    79  	rel := s.setupRelation(c, "54.1.2.3")
    80  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
    81  	c.Assert(err, jc.ErrorIsNil)
    82  	defer statetesting.AssertStop(c, w)
    83  	wc := statetesting.NewStringsWatcherC(c, w)
    84  	rel.ruw.changes <- watcher.RelationUnitsChange{}
    85  
    86  	// Initial event.
    87  	wc.AssertChange()
    88  	wc.AssertNoChange()
    89  
    90  	rel.ruw.changes <- watcher.RelationUnitsChange{
    91  		Changed: map[string]watcher.UnitSettings{
    92  			"django/0": {},
    93  		},
    94  	}
    95  	wc.AssertChange("54.1.2.3/32")
    96  	wc.AssertNoChange()
    97  
    98  	// A not found unit doesn't trigger an event.
    99  	rel.ruw.changes <- watcher.RelationUnitsChange{
   100  		Changed: map[string]watcher.UnitSettings{
   101  			"unknown/0": {},
   102  		},
   103  	}
   104  	wc.AssertNoChange()
   105  }
   106  
   107  func (s *addressWatcherSuite) TestTwoUnitsEntersScope(c *gc.C) {
   108  	rel := s.setupRelation(c, "54.1.2.3")
   109  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	defer statetesting.AssertStop(c, w)
   112  	wc := statetesting.NewStringsWatcherC(c, w)
   113  	rel.ruw.changes <- watcher.RelationUnitsChange{}
   114  
   115  	unit := newMockUnit("django/1")
   116  	unit.publicAddress = network.NewSpaceAddress("54.4.5.6")
   117  	unit.machineId = "1"
   118  	s.st.units["django/1"] = unit
   119  	s.st.machines["1"] = newMockMachine("1")
   120  
   121  	// Initial event.
   122  	wc.AssertChange()
   123  	wc.AssertNoChange()
   124  
   125  	rel.ruw.changes <- watcher.RelationUnitsChange{
   126  		Changed: map[string]watcher.UnitSettings{
   127  			"django/0": {},
   128  			"django/1": {},
   129  		},
   130  	}
   131  	wc.AssertChange("54.1.2.3/32", "54.4.5.6/32")
   132  	wc.AssertNoChange()
   133  }
   134  
   135  func (s *addressWatcherSuite) TestAnotherUnitsEntersScope(c *gc.C) {
   136  	rel := s.setupRelation(c, "54.1.2.3")
   137  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	defer statetesting.AssertStop(c, w)
   140  	wc := statetesting.NewStringsWatcherC(c, w)
   141  	rel.ruw.changes <- watcher.RelationUnitsChange{}
   142  
   143  	// Initial event.
   144  	wc.AssertChange()
   145  	wc.AssertNoChange()
   146  
   147  	rel.ruw.changes <- watcher.RelationUnitsChange{
   148  		Changed: map[string]watcher.UnitSettings{
   149  			"django/0": {},
   150  		},
   151  	}
   152  	wc.AssertChange("54.1.2.3/32")
   153  	wc.AssertNoChange()
   154  
   155  	unit := newMockUnit("django/1")
   156  	unit.publicAddress = network.NewSpaceAddress("54.4.5.6")
   157  	unit.machineId = "1"
   158  	s.st.units["django/1"] = unit
   159  	s.st.machines["1"] = newMockMachine("1")
   160  	rel.ruw.changes <- watcher.RelationUnitsChange{
   161  		Changed: map[string]watcher.UnitSettings{
   162  			"django/1": {},
   163  		},
   164  	}
   165  	wc.AssertChange("54.1.2.3/32", "54.4.5.6/32")
   166  	wc.AssertNoChange()
   167  }
   168  
   169  func (s *addressWatcherSuite) TestUnitEntersScopeNoPublicAddress(c *gc.C) {
   170  	rel := s.setupRelation(c, "")
   171  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	defer statetesting.AssertStop(c, w)
   174  	wc := statetesting.NewStringsWatcherC(c, w)
   175  	rel.ruw.changes <- watcher.RelationUnitsChange{
   176  		Changed: map[string]watcher.UnitSettings{
   177  			"django/0": {},
   178  		},
   179  	}
   180  
   181  	// Even though the unit has no public address,
   182  	// we still expect the initial event.
   183  	wc.AssertChange()
   184  	wc.AssertNoChange()
   185  
   186  	// This time no event.
   187  	rel.ruw.changes <- watcher.RelationUnitsChange{
   188  		Changed: map[string]watcher.UnitSettings{
   189  			"django/0": {},
   190  		},
   191  	}
   192  	wc.AssertNoChange()
   193  }
   194  
   195  func (s *addressWatcherSuite) TestUnitEntersScopeNotAssigned(c *gc.C) {
   196  	rel := s.setupRelation(c, "")
   197  	s.st.units["django/0"].assigned = false
   198  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	defer statetesting.AssertStop(c, w)
   201  	wc := statetesting.NewStringsWatcherC(c, w)
   202  
   203  	rel.ruw.changes <- watcher.RelationUnitsChange{
   204  		Changed: map[string]watcher.UnitSettings{
   205  			"django/0": {},
   206  		},
   207  	}
   208  
   209  	// Even though the unit is not assigned,
   210  	// we still expect the initial event.
   211  	wc.AssertChange()
   212  	wc.AssertNoChange()
   213  
   214  	// This time no event.
   215  	rel.ruw.changes <- watcher.RelationUnitsChange{
   216  		Changed: map[string]watcher.UnitSettings{
   217  			"django/0": {},
   218  		},
   219  	}
   220  	wc.AssertNoChange()
   221  }
   222  
   223  func (s *addressWatcherSuite) TestUnitLeavesScopeInitial(c *gc.C) {
   224  	rel := s.setupRelation(c, "54.1.2.3")
   225  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	defer statetesting.AssertStop(c, w)
   228  	wc := statetesting.NewStringsWatcherC(c, w)
   229  
   230  	rel.ruw.changes <- watcher.RelationUnitsChange{
   231  		Departed: []string{"django/0"},
   232  	}
   233  
   234  	// Even though the unit has not been seen via enter scope,
   235  	// we still expect the initial event.
   236  	wc.AssertChange()
   237  	wc.AssertNoChange()
   238  }
   239  
   240  func (s *addressWatcherSuite) TestUnitLeavesScope(c *gc.C) {
   241  	rel := s.setupRelation(c, "54.1.2.3")
   242  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	defer statetesting.AssertStop(c, w)
   245  	wc := statetesting.NewStringsWatcherC(c, w)
   246  	rel.ruw.changes <- watcher.RelationUnitsChange{}
   247  
   248  	unit := newMockUnit("django/1")
   249  	unit.publicAddress = network.NewSpaceAddress("54.4.5.6")
   250  	unit.machineId = "1"
   251  	s.st.units["django/1"] = unit
   252  	s.st.machines["1"] = newMockMachine("1")
   253  
   254  	// Initial event.
   255  	wc.AssertChange()
   256  	wc.AssertNoChange()
   257  
   258  	rel.ruw.changes <- watcher.RelationUnitsChange{
   259  		Changed: map[string]watcher.UnitSettings{
   260  			"django/0": {},
   261  			"django/1": {},
   262  		},
   263  	}
   264  	wc.AssertChange("54.1.2.3/32", "54.4.5.6/32")
   265  	wc.AssertNoChange()
   266  
   267  	rel.ruw.changes <- watcher.RelationUnitsChange{
   268  		Departed: []string{"django/0"},
   269  	}
   270  
   271  	wc.AssertChange("54.4.5.6/32")
   272  	wc.AssertNoChange()
   273  }
   274  
   275  func (s *addressWatcherSuite) TestTwoUnitsSameAddressOneLeaves(c *gc.C) {
   276  	rel := s.setupRelation(c, "54.1.2.3")
   277  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   278  	c.Assert(err, jc.ErrorIsNil)
   279  	defer statetesting.AssertStop(c, w)
   280  	wc := statetesting.NewStringsWatcherC(c, w)
   281  	rel.ruw.changes <- watcher.RelationUnitsChange{}
   282  
   283  	unit := newMockUnit("django/1")
   284  	unit.publicAddress = network.NewSpaceAddress("54.1.2.3")
   285  	unit.machineId = "0"
   286  	s.st.units["django/1"] = unit
   287  
   288  	// Initial event.
   289  	wc.AssertChange()
   290  	wc.AssertNoChange()
   291  
   292  	rel.ruw.changes <- watcher.RelationUnitsChange{
   293  		Changed: map[string]watcher.UnitSettings{
   294  			"django/0": {},
   295  			"django/1": {},
   296  		},
   297  	}
   298  	wc.AssertChange("54.1.2.3/32")
   299  	wc.AssertNoChange()
   300  
   301  	// One leaves, no change.
   302  	rel.ruw.changes <- watcher.RelationUnitsChange{
   303  		Departed: []string{"django/0"},
   304  	}
   305  
   306  	wc.AssertNoChange()
   307  
   308  	// Last one leaves.
   309  	rel.ruw.changes <- watcher.RelationUnitsChange{
   310  		Departed: []string{"django/1"},
   311  	}
   312  
   313  	wc.AssertChange()
   314  	wc.AssertNoChange()
   315  }
   316  
   317  func (s *addressWatcherSuite) TestSecondUnitJoinsOnSameMachine(c *gc.C) {
   318  	rel := s.setupRelation(c, "55.1.2.3")
   319  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	defer statetesting.AssertStop(c, w)
   322  	wc := statetesting.NewStringsWatcherC(c, w)
   323  	// django/0 is initially in scope
   324  	rel.ruw.changes <- watcher.RelationUnitsChange{
   325  		Changed: map[string]watcher.UnitSettings{
   326  			"django/0": {},
   327  		},
   328  	}
   329  
   330  	wc.AssertChange("55.1.2.3/32")
   331  	wc.AssertNoChange()
   332  
   333  	// Another unit joins on the same machine.
   334  	unit := newMockUnit("django/1")
   335  	unit.machineId = "0"
   336  	s.st.units["django/1"] = unit
   337  
   338  	rel.ruw.changes <- watcher.RelationUnitsChange{
   339  		Changed: map[string]watcher.UnitSettings{
   340  			"django/1": {},
   341  		},
   342  	}
   343  	// No new addresses.
   344  	wc.AssertNoChange()
   345  
   346  	// Machine 0 changes address.
   347  	s.st.units["django/0"].updateAddress("56.1.2.3")
   348  	s.st.units["django/1"].updateAddress("56.1.2.3")
   349  	s.st.machines["0"].watcher.changes <- struct{}{}
   350  
   351  	wc.AssertChange("56.1.2.3/32")
   352  	wc.AssertNoChange()
   353  }
   354  
   355  func (s *addressWatcherSuite) TestSeesMachineAddressChanges(c *gc.C) {
   356  	rel := s.setupRelation(c, "2.3.4.5")
   357  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   358  	c.Assert(err, jc.ErrorIsNil)
   359  	defer statetesting.AssertStop(c, w)
   360  	wc := statetesting.NewStringsWatcherC(c, w)
   361  	// django/0 is initially in scope
   362  	rel.ruw.changes <- watcher.RelationUnitsChange{
   363  		Changed: map[string]watcher.UnitSettings{
   364  			"django/0": {},
   365  		},
   366  	}
   367  
   368  	wc.AssertChange("2.3.4.5/32")
   369  	wc.AssertNoChange()
   370  
   371  	s.st.units["django/0"].updateAddress("5.4.3.3")
   372  	s.st.machines["0"].watcher.changes <- struct{}{}
   373  
   374  	wc.AssertChange("5.4.3.3/32")
   375  	wc.AssertNoChange()
   376  }
   377  
   378  func (s *addressWatcherSuite) TestHandlesMachineAddressChangesWithNoEffect(c *gc.C) {
   379  	rel := s.setupRelation(c, "2.3.4.5")
   380  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   381  	c.Assert(err, jc.ErrorIsNil)
   382  	defer statetesting.AssertStop(c, w)
   383  	wc := statetesting.NewStringsWatcherC(c, w)
   384  	// django/0 is initially in scope
   385  	rel.ruw.changes <- watcher.RelationUnitsChange{
   386  		Changed: map[string]watcher.UnitSettings{
   387  			"django/0": {},
   388  		},
   389  	}
   390  
   391  	wc.AssertChange("2.3.4.5/32")
   392  	wc.AssertNoChange()
   393  
   394  	// Public address for the unit stays the same (maybe some other address changed).
   395  	s.st.machines["0"].watcher.changes <- struct{}{}
   396  
   397  	wc.AssertNoChange()
   398  }
   399  
   400  func (s *addressWatcherSuite) TestHandlesUnitGoneWhenMachineAddressChanges(c *gc.C) {
   401  	rel := s.setupRelation(c, "2.3.4.5")
   402  	unit := newMockUnit("django/1")
   403  	unit.publicAddress = network.NewSpaceAddress("2.3.4.5")
   404  	unit.machineId = "0"
   405  	s.st.units["django/1"] = unit
   406  	rel.ruw.changes <- watcher.RelationUnitsChange{
   407  		Changed: map[string]watcher.UnitSettings{
   408  			"django/0": {},
   409  			"django/1": {},
   410  		},
   411  	}
   412  
   413  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   414  	c.Assert(err, jc.ErrorIsNil)
   415  	defer statetesting.AssertStop(c, w)
   416  	wc := statetesting.NewStringsWatcherC(c, w)
   417  
   418  	wc.AssertChange("2.3.4.5/32")
   419  	wc.AssertNoChange()
   420  
   421  	rel.ruw.changes <- watcher.RelationUnitsChange{
   422  		Departed: []string{"django/1"},
   423  	}
   424  	s.st.units["django/0"].updateAddress("6.7.8.9")
   425  	s.st.machines["0"].watcher.changes <- struct{}{}
   426  
   427  	wc.AssertChange("6.7.8.9/32")
   428  	wc.AssertNoChange()
   429  }
   430  
   431  func (s *addressWatcherSuite) TestModelEgressAddressUsed(c *gc.C) {
   432  	s.st.configAttrs["egress-subnets"] = "10.0.0.1/16"
   433  	rel := s.setupRelation(c, "54.1.2.3")
   434  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   435  	c.Assert(err, jc.ErrorIsNil)
   436  	defer statetesting.AssertStop(c, w)
   437  	wc := statetesting.NewStringsWatcherC(c, w)
   438  	rel.ruw.changes <- watcher.RelationUnitsChange{}
   439  
   440  	// Initial event.
   441  	wc.AssertChange()
   442  	wc.AssertNoChange()
   443  
   444  	rel.ruw.changes <- watcher.RelationUnitsChange{
   445  		Changed: map[string]watcher.UnitSettings{
   446  			"django/0": {},
   447  		},
   448  	}
   449  	wc.AssertChange("10.0.0.1/16")
   450  	wc.AssertNoChange()
   451  
   452  	// Change user configured egress addresses.
   453  	s.st.configAttrs["egress-subnets"] = "192.168.0.1/16"
   454  	s.st.modelWatcher.changes <- struct{}{}
   455  	wc.AssertChange("192.168.0.1/16")
   456  	wc.AssertNoChange()
   457  
   458  	// Reset user configured egress addresses.
   459  	s.st.configAttrs["egress-subnets"] = ""
   460  	s.st.modelWatcher.changes <- struct{}{}
   461  	wc.AssertChange("54.1.2.3/32")
   462  	wc.AssertNoChange()
   463  
   464  	// A not found unit doesn't trigger an event.
   465  	rel.ruw.changes <- watcher.RelationUnitsChange{
   466  		Changed: map[string]watcher.UnitSettings{
   467  			"unknown/0": {},
   468  		},
   469  	}
   470  	wc.AssertNoChange()
   471  }
   472  
   473  func (s *addressWatcherSuite) TestRelationEgressAddressUsed(c *gc.C) {
   474  	// Set up a model egress-address to ensure it is ignored when a relation one is used.
   475  	s.st.configAttrs["egress-subnets"] = "10.0.0.1/16"
   476  	rel := s.setupRelation(c, "54.1.2.3")
   477  	w, err := firewall.NewEgressAddressWatcher(s.st, rel, "django")
   478  	c.Assert(err, jc.ErrorIsNil)
   479  	defer statetesting.AssertStop(c, w)
   480  	wc := statetesting.NewStringsWatcherC(c, w)
   481  	rel.ruw.changes <- watcher.RelationUnitsChange{}
   482  
   483  	// Initial event.
   484  	wc.AssertChange()
   485  	wc.AssertNoChange()
   486  
   487  	// New relation ingress cidr.
   488  	rel.ew.changes <- []string{"192.168.0.0/8"}
   489  
   490  	rel.ruw.changes <- watcher.RelationUnitsChange{
   491  		Changed: map[string]watcher.UnitSettings{
   492  			"django/0": {},
   493  		},
   494  	}
   495  	wc.AssertChange("192.168.0.0/8")
   496  	wc.AssertNoChange()
   497  
   498  	// Change model egress addresses, no change since relation overrides.
   499  	s.st.configAttrs["egress-subnets"] = "192.168.0.1/16"
   500  	s.st.modelWatcher.changes <- struct{}{}
   501  	wc.AssertNoChange()
   502  
   503  	rel.ew.changes <- []string{"10.1.2.0/8"}
   504  	wc.AssertChange("10.1.2.0/8")
   505  	wc.AssertNoChange()
   506  
   507  	// A not found unit doesn't trigger an event.
   508  	rel.ruw.changes <- watcher.RelationUnitsChange{
   509  		Changed: map[string]watcher.UnitSettings{
   510  			"unknown/0": {},
   511  		},
   512  	}
   513  	wc.AssertNoChange()
   514  }