github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/firewaller/firewaller_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package firewaller_test
     5  
     6  import (
     7  	"sort"
     8  
     9  	"github.com/juju/names/v5"
    10  	jc "github.com/juju/testing/checkers"
    11  	"go.uber.org/mock/gomock"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/apiserver/common"
    15  	"github.com/juju/juju/apiserver/common/cloudspec"
    16  	commontesting "github.com/juju/juju/apiserver/common/testing"
    17  	"github.com/juju/juju/apiserver/facade"
    18  	"github.com/juju/juju/apiserver/facades/controller/firewaller"
    19  	"github.com/juju/juju/apiserver/facades/controller/firewaller/mocks"
    20  	apiservertesting "github.com/juju/juju/apiserver/testing"
    21  	"github.com/juju/juju/core/network"
    22  	"github.com/juju/juju/rpc/params"
    23  	"github.com/juju/juju/state"
    24  	statetesting "github.com/juju/juju/state/testing"
    25  )
    26  
    27  type firewallerSuite struct {
    28  	firewallerBaseSuite
    29  	*commontesting.ModelWatcherTest
    30  
    31  	firewaller *firewaller.FirewallerAPI
    32  	subnet     *state.Subnet
    33  
    34  	ctrl *gomock.Controller
    35  }
    36  
    37  var _ = gc.Suite(&firewallerSuite{})
    38  
    39  func (s *firewallerSuite) SetUpTest(c *gc.C) {
    40  	s.firewallerBaseSuite.setUpTest(c)
    41  
    42  	subnet, err := s.State.AddSubnet(network.SubnetInfo{CIDR: "10.20.30.0/24"})
    43  	c.Assert(err, jc.ErrorIsNil)
    44  	s.subnet = subnet
    45  
    46  	cloudSpecAPI := cloudspec.NewCloudSpec(
    47  		s.resources,
    48  		cloudspec.MakeCloudSpecGetterForModel(s.State),
    49  		cloudspec.MakeCloudSpecWatcherForModel(s.State),
    50  		cloudspec.MakeCloudSpecCredentialWatcherForModel(s.State),
    51  		cloudspec.MakeCloudSpecCredentialContentWatcherForModel(s.State),
    52  		common.AuthFuncForTag(s.Model.ModelTag()),
    53  	)
    54  
    55  	s.ctrl = gomock.NewController(c)
    56  	controllerConfigAPI := mocks.NewMockControllerConfigAPI(s.ctrl)
    57  	// Create a firewaller API for the machine.
    58  	firewallerAPI, err := firewaller.NewStateFirewallerAPI(
    59  		firewaller.StateShim(s.State, s.Model),
    60  		s.resources,
    61  		s.authorizer,
    62  		cloudSpecAPI,
    63  		controllerConfigAPI,
    64  	)
    65  	c.Assert(err, jc.ErrorIsNil)
    66  	s.firewaller = firewallerAPI
    67  	s.ModelWatcherTest = commontesting.NewModelWatcherTest(s.firewaller, s.State, s.resources)
    68  }
    69  
    70  func (s *firewallerSuite) TestFirewallerFailsWithNonControllerUser(c *gc.C) {
    71  	defer s.ctrl.Finish()
    72  
    73  	constructor := func(context facade.Context) error {
    74  		_, err := firewaller.NewFirewallerAPIV7(context)
    75  		return err
    76  	}
    77  	s.testFirewallerFailsWithNonControllerUser(c, constructor)
    78  }
    79  
    80  func (s *firewallerSuite) TestLife(c *gc.C) {
    81  	defer s.ctrl.Finish()
    82  
    83  	s.testLife(c, s.firewaller)
    84  }
    85  
    86  func (s *firewallerSuite) TestInstanceId(c *gc.C) {
    87  	defer s.ctrl.Finish()
    88  
    89  	s.testInstanceId(c, s.firewaller)
    90  }
    91  
    92  func (s *firewallerSuite) TestWatchModelMachines(c *gc.C) {
    93  	defer s.ctrl.Finish()
    94  
    95  	s.testWatchModelMachines(c, s.firewaller)
    96  }
    97  
    98  func (s *firewallerSuite) TestWatch(c *gc.C) {
    99  	defer s.ctrl.Finish()
   100  
   101  	s.testWatch(c, s.firewaller, cannotWatchUnits)
   102  }
   103  
   104  func (s *firewallerSuite) TestWatchUnits(c *gc.C) {
   105  	s.testWatchUnits(c, s.firewaller)
   106  }
   107  
   108  func (s *firewallerSuite) TestGetAssignedMachine(c *gc.C) {
   109  	defer s.ctrl.Finish()
   110  
   111  	s.testGetAssignedMachine(c, s.firewaller)
   112  }
   113  
   114  func (s *firewallerSuite) openPorts(c *gc.C) {
   115  	// Open some ports on the units.
   116  	allEndpoints := ""
   117  	s.mustOpenPorts(c, s.units[0], allEndpoints, []network.PortRange{
   118  		network.MustParsePortRange("1234-1400/tcp"),
   119  		network.MustParsePortRange("4321/tcp"),
   120  	})
   121  	s.mustOpenPorts(c, s.units[2], allEndpoints, []network.PortRange{
   122  		network.MustParsePortRange("1111-2222/udp"),
   123  	})
   124  }
   125  
   126  func (s *firewallerSuite) mustOpenPorts(c *gc.C, unit *state.Unit, endpointName string, portRanges []network.PortRange) {
   127  	unitPortRanges, err := unit.OpenedPortRanges()
   128  	c.Assert(err, jc.ErrorIsNil)
   129  
   130  	for _, pr := range portRanges {
   131  		unitPortRanges.Open(endpointName, pr)
   132  	}
   133  
   134  	c.Assert(s.State.ApplyOperation(unitPortRanges.Changes()), jc.ErrorIsNil)
   135  }
   136  
   137  func (s *firewallerSuite) TestWatchOpenedPorts(c *gc.C) {
   138  	defer s.ctrl.Finish()
   139  
   140  	c.Assert(s.resources.Count(), gc.Equals, 0)
   141  
   142  	s.openPorts(c)
   143  	expectChanges := []string{ // machine IDs
   144  		"0",
   145  		"2",
   146  	}
   147  
   148  	fakeModelTag := names.NewModelTag("deadbeef-deaf-face-feed-0123456789ab")
   149  	args := addFakeEntities(params.Entities{Entities: []params.Entity{
   150  		{Tag: fakeModelTag.String()},
   151  		{Tag: s.machines[0].Tag().String()},
   152  		{Tag: s.application.Tag().String()},
   153  		{Tag: s.units[0].Tag().String()},
   154  	}})
   155  	result, err := s.firewaller.WatchOpenedPorts(args)
   156  	sort.Strings(result.Results[0].Changes)
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	c.Assert(result, jc.DeepEquals, params.StringsWatchResults{
   159  		Results: []params.StringsWatchResult{
   160  			{Changes: expectChanges, StringsWatcherId: "1"},
   161  			{Error: apiservertesting.ErrUnauthorized},
   162  			{Error: apiservertesting.ErrUnauthorized},
   163  			{Error: apiservertesting.ErrUnauthorized},
   164  			{Error: apiservertesting.ErrUnauthorized},
   165  			{Error: apiservertesting.ErrUnauthorized},
   166  			{Error: apiservertesting.ErrUnauthorized},
   167  			{Error: apiservertesting.ErrUnauthorized},
   168  			{Error: apiservertesting.ErrUnauthorized},
   169  			{Error: apiservertesting.ErrUnauthorized},
   170  		},
   171  	})
   172  
   173  	// Verify the resource was registered and stop when done
   174  	c.Assert(s.resources.Count(), gc.Equals, 1)
   175  	c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1")
   176  	resource := s.resources.Get("1")
   177  	defer statetesting.AssertStop(c, resource)
   178  
   179  	// Check that the Watch has consumed the initial event ("returned" in
   180  	// the Watch call)
   181  	wc := statetesting.NewStringsWatcherC(c, resource.(state.StringsWatcher))
   182  	wc.AssertNoChange()
   183  }
   184  
   185  func (s *firewallerSuite) TestAreManuallyProvisioned(c *gc.C) {
   186  	defer s.ctrl.Finish()
   187  
   188  	m, err := s.State.AddOneMachine(state.MachineTemplate{
   189  		Base:       state.UbuntuBase("12.10"),
   190  		Jobs:       []state.MachineJob{state.JobHostUnits},
   191  		InstanceId: "2",
   192  		Nonce:      "manual:",
   193  	})
   194  	c.Assert(err, jc.ErrorIsNil)
   195  
   196  	args := addFakeEntities(params.Entities{Entities: []params.Entity{
   197  		{Tag: s.machines[0].Tag().String()},
   198  		{Tag: s.machines[1].Tag().String()},
   199  		{Tag: m.Tag().String()},
   200  		{Tag: s.application.Tag().String()},
   201  		{Tag: s.units[0].Tag().String()},
   202  	}})
   203  
   204  	result, err := s.firewaller.AreManuallyProvisioned(args)
   205  	c.Assert(err, jc.ErrorIsNil)
   206  	c.Assert(result, jc.DeepEquals, params.BoolResults{
   207  		Results: []params.BoolResult{
   208  			{Result: false, Error: nil},
   209  			{Result: false, Error: nil},
   210  			{Result: true, Error: nil},
   211  			{Result: false, Error: apiservertesting.ServerError(`"application-wordpress" is not a valid machine tag`)},
   212  			{Result: false, Error: apiservertesting.ServerError(`"unit-wordpress-0" is not a valid machine tag`)},
   213  			{Result: false, Error: apiservertesting.NotFoundError("machine 42")},
   214  			{Result: false, Error: apiservertesting.ServerError(`"unit-foo-0" is not a valid machine tag`)},
   215  			{Result: false, Error: apiservertesting.ServerError(`"application-bar" is not a valid machine tag`)},
   216  			{Result: false, Error: apiservertesting.ServerError(`"user-foo" is not a valid machine tag`)},
   217  			{Result: false, Error: apiservertesting.ServerError(`"foo-bar" is not a valid tag`)},
   218  			{Result: false, Error: apiservertesting.ServerError(`"" is not a valid tag`)},
   219  		},
   220  	})
   221  }
   222  
   223  func (s *firewallerSuite) TestGetExposeInfo(c *gc.C) {
   224  	defer s.ctrl.Finish()
   225  
   226  	// Set the application to exposed first.
   227  	err := s.application.MergeExposeSettings(map[string]state.ExposedEndpoint{
   228  		"": {
   229  			ExposeToSpaceIDs: []string{network.AlphaSpaceId},
   230  			ExposeToCIDRs:    []string{"10.0.0.0/0"},
   231  		},
   232  	})
   233  	c.Assert(err, jc.ErrorIsNil)
   234  
   235  	args := addFakeEntities(params.Entities{Entities: []params.Entity{
   236  		{Tag: s.application.Tag().String()},
   237  	}})
   238  	result, err := s.firewaller.GetExposeInfo(args)
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	c.Assert(result, jc.DeepEquals, params.ExposeInfoResults{
   241  		Results: []params.ExposeInfoResult{
   242  			{
   243  				Exposed: true,
   244  				ExposedEndpoints: map[string]params.ExposedEndpoint{
   245  					"": {
   246  						ExposeToSpaces: []string{network.AlphaSpaceId},
   247  						ExposeToCIDRs:  []string{"10.0.0.0/0"},
   248  					},
   249  				},
   250  			},
   251  			{Error: apiservertesting.ErrUnauthorized},
   252  			{Error: apiservertesting.ErrUnauthorized},
   253  			{Error: apiservertesting.NotFoundError(`application "bar"`)},
   254  			{Error: apiservertesting.ErrUnauthorized},
   255  			{Error: apiservertesting.ErrUnauthorized},
   256  			{Error: apiservertesting.ErrUnauthorized},
   257  		},
   258  	})
   259  
   260  	// Now reset the exposed flag for the application and check again.
   261  	err = s.application.ClearExposed()
   262  	c.Assert(err, jc.ErrorIsNil)
   263  
   264  	args = params.Entities{Entities: []params.Entity{
   265  		{Tag: s.application.Tag().String()},
   266  	}}
   267  	result, err = s.firewaller.GetExposeInfo(args)
   268  	c.Assert(err, jc.ErrorIsNil)
   269  	c.Assert(result, jc.DeepEquals, params.ExposeInfoResults{
   270  		Results: []params.ExposeInfoResult{
   271  			{Exposed: false},
   272  		},
   273  	})
   274  }
   275  
   276  func (s *firewallerSuite) TestWatchSubnets(c *gc.C) {
   277  	defer s.ctrl.Finish()
   278  
   279  	// Set up a spaces with two subnets
   280  	sp, err := s.State.AddSpace("outer-space", network.Id("outer-1"), nil, true)
   281  	c.Assert(err, jc.ErrorIsNil)
   282  	_, err = s.State.AddSubnet(network.SubnetInfo{
   283  		CIDR:      "192.168.0.0/24",
   284  		SpaceID:   sp.Id(),
   285  		SpaceName: sp.Name(),
   286  	})
   287  	c.Assert(err, jc.ErrorIsNil)
   288  	sub2, err := s.State.AddSubnet(network.SubnetInfo{
   289  		CIDR:      "192.168.42.0/24",
   290  		SpaceID:   sp.Id(),
   291  		SpaceName: sp.Name(),
   292  	})
   293  	c.Assert(err, jc.ErrorIsNil)
   294  
   295  	s.WaitForModelWatchersIdle(c, s.State.ModelUUID())
   296  	c.Assert(s.resources.Count(), gc.Equals, 0)
   297  
   298  	watchSubnetTags := []names.SubnetTag{
   299  		names.NewSubnetTag(sub2.ID()),
   300  	}
   301  	entities := params.Entities{
   302  		Entities: make([]params.Entity, len(watchSubnetTags)),
   303  	}
   304  	for i, tag := range watchSubnetTags {
   305  		entities.Entities[i].Tag = tag.String()
   306  	}
   307  
   308  	got, err := s.firewaller.WatchSubnets(entities)
   309  	c.Assert(err, jc.ErrorIsNil)
   310  	want := params.StringsWatchResult{
   311  		StringsWatcherId: "1",
   312  		Changes:          []string{sub2.ID()},
   313  	}
   314  	c.Assert(got.StringsWatcherId, gc.Equals, want.StringsWatcherId)
   315  	c.Assert(got.Changes, jc.SameContents, want.Changes)
   316  
   317  	// Verify the resources were registered and stop them when done.
   318  	c.Assert(s.resources.Count(), gc.Equals, 1)
   319  	resource := s.resources.Get("1")
   320  	defer statetesting.AssertStop(c, resource)
   321  
   322  	// Check that the Watch has consumed the initial event ("returned"
   323  	// in the Watch call)
   324  	wc := statetesting.NewStringsWatcherC(c, resource.(state.StringsWatcher))
   325  	wc.AssertNoChange()
   326  }