github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/firewaller/firewaller_base_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  	"github.com/juju/errors"
     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/facade"
    13  	"github.com/juju/juju/apiserver/facade/facadetest"
    14  	apiservertesting "github.com/juju/juju/apiserver/testing"
    15  	"github.com/juju/juju/core/instance"
    16  	"github.com/juju/juju/juju/testing"
    17  	"github.com/juju/juju/rpc/params"
    18  	"github.com/juju/juju/state"
    19  	statetesting "github.com/juju/juju/state/testing"
    20  )
    21  
    22  // firewallerBaseSuite implements common testing suite for all API
    23  // versions. It's not intended to be used directly or registered as a
    24  // suite, but embedded.
    25  type firewallerBaseSuite struct {
    26  	testing.JujuConnSuite
    27  
    28  	machines    []*state.Machine
    29  	application *state.Application
    30  	charm       *state.Charm
    31  	units       []*state.Unit
    32  	relations   []*state.Relation
    33  
    34  	authorizer apiservertesting.FakeAuthorizer
    35  	resources  *common.Resources
    36  }
    37  
    38  func (s *firewallerBaseSuite) setUpTest(c *gc.C) {
    39  	s.JujuConnSuite.SetUpTest(c)
    40  
    41  	// Reset previous machines and units (if any) and create 3
    42  	// machines for the tests.
    43  	s.machines = nil
    44  	s.units = nil
    45  	// Note that the specific machine ids allocated are assumed
    46  	// to be numerically consecutive from zero.
    47  	for i := 0; i <= 2; i++ {
    48  		machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
    49  		c.Check(err, jc.ErrorIsNil)
    50  		s.machines = append(s.machines, machine)
    51  	}
    52  	// Create an application and three units for these machines.
    53  	s.charm = s.AddTestingCharm(c, "wordpress")
    54  	s.application = s.AddTestingApplication(c, "wordpress", s.charm)
    55  	// Add the rest of the units and assign them.
    56  	for i := 0; i <= 2; i++ {
    57  		unit, err := s.application.AddUnit(state.AddUnitParams{})
    58  		c.Check(err, jc.ErrorIsNil)
    59  		err = unit.AssignToMachine(s.machines[i])
    60  		c.Check(err, jc.ErrorIsNil)
    61  		s.units = append(s.units, unit)
    62  	}
    63  
    64  	// Create a relation.
    65  	s.AddTestingApplication(c, "mysql", s.AddTestingCharm(c, "mysql"))
    66  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
    67  	c.Assert(err, jc.ErrorIsNil)
    68  
    69  	s.relations = make([]*state.Relation, 1)
    70  	s.relations[0], err = s.State.AddRelation(eps...)
    71  	c.Assert(err, jc.ErrorIsNil)
    72  
    73  	// Create a FakeAuthorizer so we can check permissions,
    74  	// set up assuming we logged in as the controller.
    75  	s.authorizer = apiservertesting.FakeAuthorizer{
    76  		Controller: true,
    77  	}
    78  
    79  	// Create the resource registry separately to track invocations to
    80  	// Register.
    81  	s.resources = common.NewResources()
    82  }
    83  
    84  func (s *firewallerBaseSuite) testFirewallerFailsWithNonControllerUser(
    85  	c *gc.C,
    86  	factory func(_ facade.Context) error,
    87  ) {
    88  	anAuthorizer := s.authorizer
    89  	anAuthorizer.Controller = false
    90  	ctx := facadetest.Context{
    91  		Auth_:      anAuthorizer,
    92  		Resources_: s.resources,
    93  		State_:     s.State,
    94  		StatePool_: s.StatePool,
    95  	}
    96  	err := factory(ctx)
    97  	c.Assert(err, gc.NotNil)
    98  	c.Assert(err, gc.ErrorMatches, "permission denied")
    99  }
   100  
   101  func (s *firewallerBaseSuite) testLife(
   102  	c *gc.C,
   103  	facade interface {
   104  		Life(args params.Entities) (params.LifeResults, error)
   105  	},
   106  ) {
   107  	// Unassign unit 1 from its machine, so we can change its life cycle.
   108  	err := s.units[1].UnassignFromMachine()
   109  	c.Assert(err, jc.ErrorIsNil)
   110  
   111  	err = s.machines[1].EnsureDead()
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	s.assertLife(c, 0, state.Alive)
   114  	s.assertLife(c, 1, state.Dead)
   115  	s.assertLife(c, 2, state.Alive)
   116  
   117  	args := addFakeEntities(params.Entities{Entities: []params.Entity{
   118  		{Tag: s.machines[0].Tag().String()},
   119  		{Tag: s.machines[1].Tag().String()},
   120  		{Tag: s.machines[2].Tag().String()},
   121  		{Tag: s.relations[0].Tag().String()},
   122  	}})
   123  	result, err := facade.Life(args)
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	c.Assert(result, jc.DeepEquals, params.LifeResults{
   126  		Results: []params.LifeResult{
   127  			{Life: "alive"},
   128  			{Life: "dead"},
   129  			{Life: "alive"},
   130  			{Life: "alive"},
   131  			{Error: apiservertesting.NotFoundError("machine 42")},
   132  			{Error: apiservertesting.NotFoundError(`unit "foo/0"`)},
   133  			{Error: apiservertesting.NotFoundError(`application "bar"`)},
   134  			{Error: apiservertesting.ErrUnauthorized},
   135  			{Error: apiservertesting.ErrUnauthorized},
   136  			{Error: apiservertesting.ErrUnauthorized},
   137  		},
   138  	})
   139  
   140  	// Remove a machine and make sure it's detected.
   141  	err = s.machines[1].Remove()
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	err = s.machines[1].Refresh()
   144  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   145  
   146  	args = params.Entities{
   147  		Entities: []params.Entity{
   148  			{Tag: s.machines[1].Tag().String()},
   149  		},
   150  	}
   151  	result, err = facade.Life(args)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	c.Assert(result, jc.DeepEquals, params.LifeResults{
   154  		Results: []params.LifeResult{
   155  			{Error: apiservertesting.NotFoundError("machine 1")},
   156  		},
   157  	})
   158  }
   159  
   160  func (s *firewallerBaseSuite) testInstanceId(
   161  	c *gc.C,
   162  	facade interface {
   163  		InstanceId(args params.Entities) (params.StringResults, error)
   164  	},
   165  ) {
   166  	// Provision 2 machines first.
   167  	err := s.machines[0].SetProvisioned("i-am", "", "fake_nonce", nil)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	hwChars := instance.MustParseHardware("arch=s390x", "mem=4G")
   170  	err = s.machines[1].SetProvisioned("i-am-not", "", "fake_nonce", &hwChars)
   171  	c.Assert(err, jc.ErrorIsNil)
   172  
   173  	args := addFakeEntities(params.Entities{Entities: []params.Entity{
   174  		{Tag: s.machines[0].Tag().String()},
   175  		{Tag: s.machines[1].Tag().String()},
   176  		{Tag: s.machines[2].Tag().String()},
   177  		{Tag: s.application.Tag().String()},
   178  		{Tag: s.units[2].Tag().String()},
   179  	}})
   180  	result, err := facade.InstanceId(args)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	c.Assert(result, jc.DeepEquals, params.StringResults{
   183  		Results: []params.StringResult{
   184  			{Result: "i-am"},
   185  			{Result: "i-am-not"},
   186  			{Error: apiservertesting.NotProvisionedError("2")},
   187  			{Error: apiservertesting.ErrUnauthorized},
   188  			{Error: apiservertesting.ErrUnauthorized},
   189  			{Error: apiservertesting.NotFoundError("machine 42")},
   190  			{Error: apiservertesting.ErrUnauthorized},
   191  			{Error: apiservertesting.ErrUnauthorized},
   192  			{Error: apiservertesting.ErrUnauthorized},
   193  			{Error: apiservertesting.ErrUnauthorized},
   194  			{Error: apiservertesting.ErrUnauthorized},
   195  		},
   196  	})
   197  }
   198  
   199  func (s *firewallerBaseSuite) testWatchModelMachines(
   200  	c *gc.C,
   201  	facade interface {
   202  		WatchModelMachines() (params.StringsWatchResult, error)
   203  	},
   204  ) {
   205  	c.Assert(s.resources.Count(), gc.Equals, 0)
   206  
   207  	got, err := facade.WatchModelMachines()
   208  	c.Assert(err, jc.ErrorIsNil)
   209  	want := params.StringsWatchResult{
   210  		StringsWatcherId: "1",
   211  		Changes:          []string{"0", "1", "2"},
   212  	}
   213  	c.Assert(got.StringsWatcherId, gc.Equals, want.StringsWatcherId)
   214  	c.Assert(got.Changes, jc.SameContents, want.Changes)
   215  
   216  	// Verify the resources were registered and stop them when done.
   217  	c.Assert(s.resources.Count(), gc.Equals, 1)
   218  	resource := s.resources.Get("1")
   219  	defer statetesting.AssertStop(c, resource)
   220  
   221  	// Check that the Watch has consumed the initial event ("returned"
   222  	// in the Watch call)
   223  	wc := statetesting.NewStringsWatcherC(c, resource.(state.StringsWatcher))
   224  	wc.AssertNoChange()
   225  }
   226  
   227  const (
   228  	cannotWatchUnits = false
   229  )
   230  
   231  func (s *firewallerBaseSuite) testWatch(
   232  	c *gc.C,
   233  	watcher interface {
   234  		Watch(args params.Entities) (params.NotifyWatchResults, error)
   235  	},
   236  	allowUnits bool,
   237  ) {
   238  	c.Assert(s.resources.Count(), gc.Equals, 0)
   239  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
   240  
   241  	args := addFakeEntities(params.Entities{Entities: []params.Entity{
   242  		{Tag: s.machines[0].Tag().String()},
   243  		{Tag: s.application.Tag().String()},
   244  		{Tag: s.units[0].Tag().String()},
   245  	}})
   246  	result, err := watcher.Watch(args)
   247  	c.Assert(err, jc.ErrorIsNil)
   248  	if allowUnits {
   249  		c.Assert(result, jc.DeepEquals, params.NotifyWatchResults{
   250  			Results: []params.NotifyWatchResult{
   251  				{Error: apiservertesting.ErrUnauthorized},
   252  				{NotifyWatcherId: "1"},
   253  				{NotifyWatcherId: "2"},
   254  				{Error: apiservertesting.ErrUnauthorized},
   255  				{Error: apiservertesting.NotFoundError(`unit "foo/0"`)},
   256  				{Error: apiservertesting.NotFoundError(`application "bar"`)},
   257  				{Error: apiservertesting.ErrUnauthorized},
   258  				{Error: apiservertesting.ErrUnauthorized},
   259  				{Error: apiservertesting.ErrUnauthorized},
   260  			},
   261  		})
   262  	} else {
   263  		c.Assert(result, jc.DeepEquals, params.NotifyWatchResults{
   264  			Results: []params.NotifyWatchResult{
   265  				{Error: apiservertesting.ErrUnauthorized},
   266  				{NotifyWatcherId: "1"},
   267  				{Error: apiservertesting.ErrUnauthorized},
   268  				{Error: apiservertesting.ErrUnauthorized},
   269  				{Error: apiservertesting.ErrUnauthorized},
   270  				{Error: apiservertesting.NotFoundError(`application "bar"`)},
   271  				{Error: apiservertesting.ErrUnauthorized},
   272  				{Error: apiservertesting.ErrUnauthorized},
   273  				{Error: apiservertesting.ErrUnauthorized},
   274  			},
   275  		})
   276  	}
   277  
   278  	// Verify the resources were registered and stop when done.
   279  	if allowUnits {
   280  		c.Assert(s.resources.Count(), gc.Equals, 2)
   281  	} else {
   282  		c.Assert(s.resources.Count(), gc.Equals, 1)
   283  	}
   284  	c.Assert(result.Results[1].NotifyWatcherId, gc.Equals, "1")
   285  	watcher1 := s.resources.Get("1")
   286  	defer statetesting.AssertStop(c, watcher1)
   287  	var watcher2 facade.Resource
   288  	if allowUnits {
   289  		c.Assert(result.Results[2].NotifyWatcherId, gc.Equals, "2")
   290  		watcher2 = s.resources.Get("2")
   291  		defer statetesting.AssertStop(c, watcher2)
   292  	}
   293  
   294  	// Check that the Watch has consumed the initial event ("returned" in
   295  	// the Watch call)
   296  	wc1 := statetesting.NewNotifyWatcherC(c, watcher1.(state.NotifyWatcher))
   297  	wc1.AssertNoChange()
   298  	if allowUnits {
   299  		wc2 := statetesting.NewNotifyWatcherC(c, watcher2.(state.NotifyWatcher))
   300  		wc2.AssertNoChange()
   301  	}
   302  }
   303  
   304  func (s *firewallerBaseSuite) testWatchUnits(
   305  	c *gc.C,
   306  	facade interface {
   307  		WatchUnits(args params.Entities) (params.StringsWatchResults, error)
   308  	},
   309  ) {
   310  	c.Assert(s.resources.Count(), gc.Equals, 0)
   311  
   312  	args := addFakeEntities(params.Entities{Entities: []params.Entity{
   313  		{Tag: s.machines[0].Tag().String()},
   314  		{Tag: s.application.Tag().String()},
   315  		{Tag: s.units[0].Tag().String()},
   316  	}})
   317  	result, err := facade.WatchUnits(args)
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	c.Assert(result, jc.DeepEquals, params.StringsWatchResults{
   320  		Results: []params.StringsWatchResult{
   321  			{Changes: []string{"wordpress/0"}, StringsWatcherId: "1"},
   322  			{Error: apiservertesting.ErrUnauthorized},
   323  			{Error: apiservertesting.ErrUnauthorized},
   324  			{Error: apiservertesting.NotFoundError("machine 42")},
   325  			{Error: apiservertesting.ErrUnauthorized},
   326  			{Error: apiservertesting.ErrUnauthorized},
   327  			{Error: apiservertesting.ErrUnauthorized},
   328  			{Error: apiservertesting.ErrUnauthorized},
   329  			{Error: apiservertesting.ErrUnauthorized},
   330  		},
   331  	})
   332  
   333  	// Verify the resource was registered and stop when done
   334  	c.Assert(s.resources.Count(), gc.Equals, 1)
   335  	c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1")
   336  	resource := s.resources.Get("1")
   337  	defer statetesting.AssertStop(c, resource)
   338  
   339  	// Check that the Watch has consumed the initial event ("returned" in
   340  	// the Watch call)
   341  	wc := statetesting.NewStringsWatcherC(c, resource.(state.StringsWatcher))
   342  	wc.AssertNoChange()
   343  }
   344  
   345  func (s *firewallerBaseSuite) testGetAssignedMachine(
   346  	c *gc.C,
   347  	facade interface {
   348  		GetAssignedMachine(args params.Entities) (params.StringResults, error)
   349  	},
   350  ) {
   351  	// Unassign a unit first.
   352  	err := s.units[2].UnassignFromMachine()
   353  	c.Assert(err, jc.ErrorIsNil)
   354  
   355  	args := addFakeEntities(params.Entities{Entities: []params.Entity{
   356  		{Tag: s.units[0].Tag().String()},
   357  		{Tag: s.units[1].Tag().String()},
   358  		{Tag: s.units[2].Tag().String()},
   359  	}})
   360  	result, err := facade.GetAssignedMachine(args)
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	c.Assert(result, jc.DeepEquals, params.StringResults{
   363  		Results: []params.StringResult{
   364  			{Result: s.machines[0].Tag().String()},
   365  			{Result: s.machines[1].Tag().String()},
   366  			{Error: apiservertesting.NotAssignedError("wordpress/2")},
   367  			{Error: apiservertesting.ErrUnauthorized},
   368  			{Error: apiservertesting.NotFoundError(`unit "foo/0"`)},
   369  			{Error: apiservertesting.ErrUnauthorized},
   370  			{Error: apiservertesting.ErrUnauthorized},
   371  			{Error: apiservertesting.ErrUnauthorized},
   372  			{Error: apiservertesting.ErrUnauthorized},
   373  		},
   374  	})
   375  
   376  	// Now reset assign unit 2 again and check.
   377  	err = s.units[2].AssignToMachine(s.machines[0])
   378  	c.Assert(err, jc.ErrorIsNil)
   379  
   380  	args = params.Entities{Entities: []params.Entity{
   381  		{Tag: s.units[2].Tag().String()},
   382  	}}
   383  	result, err = facade.GetAssignedMachine(args)
   384  	c.Assert(err, jc.ErrorIsNil)
   385  	c.Assert(result, jc.DeepEquals, params.StringResults{
   386  		Results: []params.StringResult{
   387  			{Result: s.machines[0].Tag().String()},
   388  		},
   389  	})
   390  }
   391  
   392  func (s *firewallerBaseSuite) assertLife(c *gc.C, index int, expectLife state.Life) {
   393  	err := s.machines[index].Refresh()
   394  	c.Assert(err, jc.ErrorIsNil)
   395  	c.Assert(s.machines[index].Life(), gc.Equals, expectLife)
   396  }
   397  
   398  var commonFakeEntities = []params.Entity{
   399  	{Tag: "machine-42"},
   400  	{Tag: "unit-foo-0"},
   401  	{Tag: "application-bar"},
   402  	{Tag: "user-foo"},
   403  	{Tag: "foo-bar"},
   404  	{Tag: ""},
   405  }
   406  
   407  func addFakeEntities(actual params.Entities) params.Entities {
   408  	for _, entity := range commonFakeEntities {
   409  		actual.Entities = append(actual.Entities, entity)
   410  	}
   411  	return actual
   412  }