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