github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/apiserver/instancepoller/instancepoller_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package instancepoller_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/apiserver/common"
    14  	"github.com/juju/juju/apiserver/instancepoller"
    15  	"github.com/juju/juju/apiserver/params"
    16  	apiservertesting "github.com/juju/juju/apiserver/testing"
    17  	"github.com/juju/juju/network"
    18  	"github.com/juju/juju/state"
    19  	statetesting "github.com/juju/juju/state/testing"
    20  	coretesting "github.com/juju/juju/testing"
    21  )
    22  
    23  type InstancePollerSuite struct {
    24  	coretesting.BaseSuite
    25  
    26  	st         *mockState
    27  	api        *instancepoller.InstancePollerAPI
    28  	authoriser apiservertesting.FakeAuthorizer
    29  	resources  *common.Resources
    30  
    31  	machineEntities     params.Entities
    32  	machineErrorResults params.ErrorResults
    33  
    34  	mixedEntities     params.Entities
    35  	mixedErrorResults params.ErrorResults
    36  }
    37  
    38  var _ = gc.Suite(&InstancePollerSuite{})
    39  
    40  func (s *InstancePollerSuite) SetUpTest(c *gc.C) {
    41  	s.BaseSuite.SetUpTest(c)
    42  
    43  	s.authoriser = apiservertesting.FakeAuthorizer{
    44  		EnvironManager: true,
    45  	}
    46  	s.resources = common.NewResources()
    47  	s.AddCleanup(func(*gc.C) { s.resources.StopAll() })
    48  
    49  	s.st = NewMockState()
    50  	instancepoller.PatchState(s, s.st)
    51  
    52  	var err error
    53  	s.api, err = instancepoller.NewInstancePollerAPI(nil, s.resources, s.authoriser)
    54  	c.Assert(err, jc.ErrorIsNil)
    55  
    56  	s.machineEntities = params.Entities{
    57  		Entities: []params.Entity{
    58  			{Tag: "machine-1"},
    59  			{Tag: "machine-2"},
    60  			{Tag: "machine-3"},
    61  		}}
    62  	s.machineErrorResults = params.ErrorResults{
    63  		Results: []params.ErrorResult{
    64  			{Error: apiservertesting.ServerError("pow!")},
    65  			{Error: apiservertesting.ServerError("FAIL")},
    66  			{Error: apiservertesting.NotProvisionedError("42")},
    67  		}}
    68  
    69  	s.mixedEntities = params.Entities{
    70  		Entities: []params.Entity{
    71  			{Tag: "machine-1"},
    72  			{Tag: "machine-2"},
    73  			{Tag: "machine-42"},
    74  			{Tag: "service-unknown"},
    75  			{Tag: "invalid-tag"},
    76  			{Tag: "unit-missing-1"},
    77  			{Tag: ""},
    78  			{Tag: "42"},
    79  		}}
    80  	s.mixedErrorResults = params.ErrorResults{
    81  		Results: []params.ErrorResult{
    82  			{Error: nil},
    83  			{Error: nil},
    84  			{Error: apiservertesting.NotFoundError("machine 42")},
    85  			{Error: apiservertesting.ServerError(`"service-unknown" is not a valid machine tag`)},
    86  			{Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)},
    87  			{Error: apiservertesting.ServerError(`"unit-missing-1" is not a valid machine tag`)},
    88  			{Error: apiservertesting.ServerError(`"" is not a valid tag`)},
    89  			{Error: apiservertesting.ServerError(`"42" is not a valid tag`)},
    90  		}}
    91  }
    92  
    93  func (s *InstancePollerSuite) TestNewInstancePollerAPIRequiresEnvironManager(c *gc.C) {
    94  	anAuthoriser := s.authoriser
    95  	anAuthoriser.EnvironManager = false
    96  	api, err := instancepoller.NewInstancePollerAPI(nil, s.resources, anAuthoriser)
    97  	c.Assert(api, gc.IsNil)
    98  	c.Assert(err, gc.ErrorMatches, "permission denied")
    99  }
   100  
   101  func (s *InstancePollerSuite) TestEnvironConfigFailure(c *gc.C) {
   102  	s.st.SetErrors(errors.New("boom"))
   103  
   104  	result, err := s.api.EnvironConfig()
   105  	c.Assert(err, gc.ErrorMatches, "boom")
   106  	c.Assert(result, jc.DeepEquals, params.EnvironConfigResult{})
   107  
   108  	s.st.CheckCallNames(c, "EnvironConfig")
   109  }
   110  
   111  func (s *InstancePollerSuite) TestEnvironConfigSuccess(c *gc.C) {
   112  	envConfig := coretesting.EnvironConfig(c)
   113  	s.st.SetConfig(c, envConfig)
   114  
   115  	result, err := s.api.EnvironConfig()
   116  	c.Assert(err, jc.ErrorIsNil)
   117  	c.Assert(result, jc.DeepEquals, params.EnvironConfigResult{
   118  		Config: envConfig.AllAttrs(),
   119  	})
   120  
   121  	s.st.CheckCallNames(c, "EnvironConfig")
   122  }
   123  
   124  func (s *InstancePollerSuite) TestWatchForEnvironConfigChangesFailure(c *gc.C) {
   125  	// Force the Changes() method of the mock watcher to return a
   126  	// closed channel by setting an error.
   127  	s.st.SetErrors(errors.New("boom"))
   128  
   129  	result, err := s.api.WatchForEnvironConfigChanges()
   130  	c.Assert(err, gc.ErrorMatches, "boom")
   131  	c.Assert(result, jc.DeepEquals, params.NotifyWatchResult{})
   132  
   133  	c.Assert(s.resources.Count(), gc.Equals, 0) // no watcher registered
   134  	s.st.CheckCallNames(c, "WatchForEnvironConfigChanges")
   135  }
   136  
   137  func (s *InstancePollerSuite) TestWatchForEnvironConfigChangesSuccess(c *gc.C) {
   138  	result, err := s.api.WatchForEnvironConfigChanges()
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	c.Assert(result, jc.DeepEquals, params.NotifyWatchResult{
   141  		Error: nil, NotifyWatcherId: "1",
   142  	})
   143  
   144  	// Verify the watcher resource was registered.
   145  	c.Assert(s.resources.Count(), gc.Equals, 1)
   146  	resource := s.resources.Get("1")
   147  	defer statetesting.AssertStop(c, resource)
   148  
   149  	// Check that the watcher has consumed the initial event
   150  	wc := statetesting.NewNotifyWatcherC(c, s.st, resource.(state.NotifyWatcher))
   151  	wc.AssertNoChange()
   152  
   153  	s.st.CheckCallNames(c, "WatchForEnvironConfigChanges")
   154  
   155  	// Try changing the config to verify an event is reported.
   156  	envConfig := coretesting.EnvironConfig(c)
   157  	s.st.SetConfig(c, envConfig)
   158  	wc.AssertOneChange()
   159  }
   160  
   161  func (s *InstancePollerSuite) TestWatchEnvironMachinesFailure(c *gc.C) {
   162  	// Force the Changes() method of the mock watcher to return a
   163  	// closed channel by setting an error.
   164  	s.st.SetErrors(errors.Errorf("boom"))
   165  
   166  	result, err := s.api.WatchEnvironMachines()
   167  	c.Assert(err, gc.ErrorMatches, "cannot obtain initial environment machines: boom")
   168  	c.Assert(result, jc.DeepEquals, params.StringsWatchResult{})
   169  
   170  	c.Assert(s.resources.Count(), gc.Equals, 0) // no watcher registered
   171  	s.st.CheckCallNames(c, "WatchEnvironMachines")
   172  }
   173  
   174  func (s *InstancePollerSuite) TestWatchEnvironMachinesSuccess(c *gc.C) {
   175  	// Add a couple of machines.
   176  	s.st.SetMachineInfo(c, machineInfo{id: "2"})
   177  	s.st.SetMachineInfo(c, machineInfo{id: "1"})
   178  
   179  	expectedResult := params.StringsWatchResult{
   180  		Error:            nil,
   181  		StringsWatcherId: "1",
   182  		Changes:          []string{"1", "2"}, // initial event (sorted ids)
   183  	}
   184  	result, err := s.api.WatchEnvironMachines()
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	c.Assert(result, jc.DeepEquals, expectedResult)
   187  
   188  	// Verify the watcher resource was registered.
   189  	c.Assert(s.resources.Count(), gc.Equals, 1)
   190  	resource1 := s.resources.Get("1")
   191  	defer func() {
   192  		if resource1 != nil {
   193  			statetesting.AssertStop(c, resource1)
   194  		}
   195  	}()
   196  
   197  	// Check that the watcher has consumed the initial event
   198  	wc1 := statetesting.NewStringsWatcherC(c, s.st, resource1.(state.StringsWatcher))
   199  	wc1.AssertNoChange()
   200  
   201  	s.st.CheckCallNames(c, "WatchEnvironMachines")
   202  
   203  	// Add another watcher to verify events coalescence.
   204  	result, err = s.api.WatchEnvironMachines()
   205  	c.Assert(err, jc.ErrorIsNil)
   206  	expectedResult.StringsWatcherId = "2"
   207  	c.Assert(result, jc.DeepEquals, expectedResult)
   208  	s.st.CheckCallNames(c, "WatchEnvironMachines", "WatchEnvironMachines")
   209  	c.Assert(s.resources.Count(), gc.Equals, 2)
   210  	resource2 := s.resources.Get("2")
   211  	defer statetesting.AssertStop(c, resource2)
   212  	wc2 := statetesting.NewStringsWatcherC(c, s.st, resource2.(state.StringsWatcher))
   213  	wc2.AssertNoChange()
   214  
   215  	// Remove machine 1, check it's reported.
   216  	s.st.RemoveMachine(c, "1")
   217  	wc1.AssertChangeInSingleEvent("1")
   218  
   219  	// Make separate changes, check they're combined.
   220  	s.st.SetMachineInfo(c, machineInfo{id: "2", life: state.Dying})
   221  	s.st.SetMachineInfo(c, machineInfo{id: "3"})
   222  	s.st.RemoveMachine(c, "42") // ignored
   223  	wc1.AssertChangeInSingleEvent("2", "3")
   224  	wc2.AssertChangeInSingleEvent("1", "2", "3")
   225  
   226  	// Stop the first watcher and assert its changes chan is closed.
   227  	c.Assert(resource1.Stop(), jc.ErrorIsNil)
   228  	wc1.AssertClosed()
   229  	resource1 = nil
   230  }
   231  
   232  func (s *InstancePollerSuite) TestLifeSuccess(c *gc.C) {
   233  	s.st.SetMachineInfo(c, machineInfo{id: "1", life: state.Alive})
   234  	s.st.SetMachineInfo(c, machineInfo{id: "2", life: state.Dying})
   235  
   236  	result, err := s.api.Life(s.mixedEntities)
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	c.Assert(result, jc.DeepEquals, params.LifeResults{
   239  		Results: []params.LifeResult{
   240  			{Life: params.Alive},
   241  			{Life: params.Dying},
   242  			{Error: apiservertesting.NotFoundError("machine 42")},
   243  			{Error: apiservertesting.ErrUnauthorized},
   244  			{Error: apiservertesting.ErrUnauthorized},
   245  			{Error: apiservertesting.ErrUnauthorized},
   246  			{Error: apiservertesting.ErrUnauthorized},
   247  			{Error: apiservertesting.ErrUnauthorized},
   248  		}},
   249  	)
   250  
   251  	s.st.CheckFindEntityCall(c, 0, "1")
   252  	s.st.CheckCall(c, 1, "Life")
   253  	s.st.CheckFindEntityCall(c, 2, "2")
   254  	s.st.CheckCall(c, 3, "Life")
   255  	s.st.CheckFindEntityCall(c, 4, "42")
   256  }
   257  
   258  func (s *InstancePollerSuite) TestLifeFailure(c *gc.C) {
   259  	s.st.SetErrors(
   260  		errors.New("pow!"),                   // m1 := FindEntity("1"); Life not called
   261  		nil,                                  // m2 := FindEntity("2")
   262  		errors.New("FAIL"),                   // m2.Life() - unused
   263  		errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved)
   264  	)
   265  	s.st.SetMachineInfo(c, machineInfo{id: "1", life: state.Alive})
   266  	s.st.SetMachineInfo(c, machineInfo{id: "2", life: state.Dead})
   267  	s.st.SetMachineInfo(c, machineInfo{id: "3", life: state.Dying})
   268  
   269  	result, err := s.api.Life(s.machineEntities)
   270  	c.Assert(err, jc.ErrorIsNil)
   271  	c.Assert(result, jc.DeepEquals, params.LifeResults{
   272  		Results: []params.LifeResult{
   273  			{Error: apiservertesting.ServerError("pow!")},
   274  			{Life: params.Dead},
   275  			{Error: apiservertesting.NotProvisionedError("42")},
   276  		}},
   277  	)
   278  
   279  	s.st.CheckFindEntityCall(c, 0, "1")
   280  	s.st.CheckFindEntityCall(c, 1, "2")
   281  	s.st.CheckCall(c, 2, "Life")
   282  	s.st.CheckFindEntityCall(c, 3, "3")
   283  }
   284  
   285  func (s *InstancePollerSuite) TestInstanceIdSuccess(c *gc.C) {
   286  	s.st.SetMachineInfo(c, machineInfo{id: "1", instanceId: "i-foo"})
   287  	s.st.SetMachineInfo(c, machineInfo{id: "2", instanceId: ""})
   288  
   289  	result, err := s.api.InstanceId(s.mixedEntities)
   290  	c.Assert(err, jc.ErrorIsNil)
   291  	c.Assert(result, jc.DeepEquals, params.StringResults{
   292  		Results: []params.StringResult{
   293  			{Result: "i-foo"},
   294  			{Result: ""},
   295  			{Error: apiservertesting.NotFoundError("machine 42")},
   296  			{Error: apiservertesting.ErrUnauthorized},
   297  			{Error: apiservertesting.ErrUnauthorized},
   298  			{Error: apiservertesting.ErrUnauthorized},
   299  			{Error: apiservertesting.ErrUnauthorized},
   300  			{Error: apiservertesting.ErrUnauthorized},
   301  		}},
   302  	)
   303  
   304  	s.st.CheckFindEntityCall(c, 0, "1")
   305  	s.st.CheckCall(c, 1, "InstanceId")
   306  	s.st.CheckFindEntityCall(c, 2, "2")
   307  	s.st.CheckCall(c, 3, "InstanceId")
   308  	s.st.CheckFindEntityCall(c, 4, "42")
   309  }
   310  
   311  func (s *InstancePollerSuite) TestInstanceIdFailure(c *gc.C) {
   312  	s.st.SetErrors(
   313  		errors.New("pow!"),                   // m1 := FindEntity("1"); InstanceId not called
   314  		nil,                                  // m2 := FindEntity("2")
   315  		errors.New("FAIL"),                   // m2.InstanceId()
   316  		errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved)
   317  	)
   318  	s.st.SetMachineInfo(c, machineInfo{id: "1", instanceId: ""})
   319  	s.st.SetMachineInfo(c, machineInfo{id: "2", instanceId: "i-bar"})
   320  
   321  	result, err := s.api.InstanceId(s.machineEntities)
   322  	c.Assert(err, jc.ErrorIsNil)
   323  	c.Assert(result, jc.DeepEquals, params.StringResults{
   324  		Results: []params.StringResult{
   325  			{Error: apiservertesting.ServerError("pow!")},
   326  			{Error: apiservertesting.ServerError("FAIL")},
   327  			{Error: apiservertesting.NotProvisionedError("42")},
   328  		}},
   329  	)
   330  
   331  	s.st.CheckFindEntityCall(c, 0, "1")
   332  	s.st.CheckFindEntityCall(c, 1, "2")
   333  	s.st.CheckCall(c, 2, "InstanceId")
   334  	s.st.CheckFindEntityCall(c, 3, "3")
   335  }
   336  
   337  func (s *InstancePollerSuite) TestStatusSuccess(c *gc.C) {
   338  	now := time.Now()
   339  	s1 := state.StatusInfo{
   340  		Status:  state.StatusError,
   341  		Message: "not really",
   342  		Data: map[string]interface{}{
   343  			"price": 4.2,
   344  			"bool":  false,
   345  			"bar":   []string{"a", "b"},
   346  		},
   347  		Since: &now,
   348  	}
   349  	s2 := state.StatusInfo{}
   350  	s.st.SetMachineInfo(c, machineInfo{id: "1", status: s1})
   351  	s.st.SetMachineInfo(c, machineInfo{id: "2", status: s2})
   352  
   353  	result, err := s.api.Status(s.mixedEntities)
   354  	c.Assert(err, jc.ErrorIsNil)
   355  	c.Assert(result, jc.DeepEquals, params.StatusResults{
   356  		Results: []params.StatusResult{
   357  			{
   358  				Status: params.StatusError,
   359  				Info:   s1.Message,
   360  				Data:   s1.Data,
   361  				Since:  s1.Since,
   362  			},
   363  			{Status: "", Info: "", Data: nil, Since: nil},
   364  			{Error: apiservertesting.NotFoundError("machine 42")},
   365  			{Error: apiservertesting.ErrUnauthorized},
   366  			{Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)},
   367  			{Error: apiservertesting.ErrUnauthorized},
   368  			{Error: apiservertesting.ServerError(`"" is not a valid tag`)},
   369  			{Error: apiservertesting.ServerError(`"42" is not a valid tag`)},
   370  		}},
   371  	)
   372  
   373  	s.st.CheckFindEntityCall(c, 0, "1")
   374  	s.st.CheckCall(c, 1, "Status")
   375  	s.st.CheckFindEntityCall(c, 2, "2")
   376  	s.st.CheckCall(c, 3, "Status")
   377  	s.st.CheckFindEntityCall(c, 4, "42")
   378  }
   379  
   380  func (s *InstancePollerSuite) TestStatusFailure(c *gc.C) {
   381  	s.st.SetErrors(
   382  		errors.New("pow!"),                   // m1 := FindEntity("1"); Status not called
   383  		nil,                                  // m2 := FindEntity("2")
   384  		errors.New("FAIL"),                   // m2.Status()
   385  		errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved)
   386  	)
   387  	s.st.SetMachineInfo(c, machineInfo{id: "1"})
   388  	s.st.SetMachineInfo(c, machineInfo{id: "2"})
   389  
   390  	result, err := s.api.Status(s.machineEntities)
   391  	c.Assert(err, jc.ErrorIsNil)
   392  	c.Assert(result, jc.DeepEquals, params.StatusResults{
   393  		Results: []params.StatusResult{
   394  			{Error: apiservertesting.ServerError("pow!")},
   395  			{Error: apiservertesting.ServerError("FAIL")},
   396  			{Error: apiservertesting.NotProvisionedError("42")},
   397  		}},
   398  	)
   399  
   400  	s.st.CheckFindEntityCall(c, 0, "1")
   401  	s.st.CheckFindEntityCall(c, 1, "2")
   402  	s.st.CheckCall(c, 2, "Status")
   403  	s.st.CheckFindEntityCall(c, 3, "3")
   404  }
   405  
   406  func (s *InstancePollerSuite) TestProviderAddressesSuccess(c *gc.C) {
   407  	addrs := network.NewAddresses("0.1.2.3", "127.0.0.1", "8.8.8.8")
   408  	expectedAddresses := params.FromNetworkAddresses(addrs)
   409  	s.st.SetMachineInfo(c, machineInfo{id: "1", providerAddresses: addrs})
   410  	s.st.SetMachineInfo(c, machineInfo{id: "2", providerAddresses: nil})
   411  
   412  	result, err := s.api.ProviderAddresses(s.mixedEntities)
   413  	c.Assert(err, jc.ErrorIsNil)
   414  	c.Assert(result, jc.DeepEquals, params.MachineAddressesResults{
   415  		Results: []params.MachineAddressesResult{
   416  			{Addresses: expectedAddresses},
   417  			{Addresses: nil},
   418  			{Error: apiservertesting.NotFoundError("machine 42")},
   419  			{Error: apiservertesting.ServerError(`"service-unknown" is not a valid machine tag`)},
   420  			{Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)},
   421  			{Error: apiservertesting.ServerError(`"unit-missing-1" is not a valid machine tag`)},
   422  			{Error: apiservertesting.ServerError(`"" is not a valid tag`)},
   423  			{Error: apiservertesting.ServerError(`"42" is not a valid tag`)},
   424  		}},
   425  	)
   426  
   427  	s.st.CheckFindEntityCall(c, 0, "1")
   428  	s.st.CheckCall(c, 1, "ProviderAddresses")
   429  	s.st.CheckFindEntityCall(c, 2, "2")
   430  	s.st.CheckCall(c, 3, "ProviderAddresses")
   431  	s.st.CheckFindEntityCall(c, 4, "42")
   432  }
   433  
   434  func (s *InstancePollerSuite) TestProviderAddressesFailure(c *gc.C) {
   435  	s.st.SetErrors(
   436  		errors.New("pow!"),                   // m1 := FindEntity("1")
   437  		nil,                                  // m2 := FindEntity("2")
   438  		errors.New("FAIL"),                   // m2.ProviderAddresses()- unused
   439  		errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved)
   440  	)
   441  	s.st.SetMachineInfo(c, machineInfo{id: "1"})
   442  	s.st.SetMachineInfo(c, machineInfo{id: "2"})
   443  
   444  	result, err := s.api.ProviderAddresses(s.machineEntities)
   445  	c.Assert(err, jc.ErrorIsNil)
   446  	c.Assert(result, jc.DeepEquals, params.MachineAddressesResults{
   447  		Results: []params.MachineAddressesResult{
   448  			{Error: apiservertesting.ServerError("pow!")},
   449  			{Addresses: nil},
   450  			{Error: apiservertesting.NotProvisionedError("42")},
   451  		}},
   452  	)
   453  
   454  	s.st.CheckFindEntityCall(c, 0, "1")
   455  	s.st.CheckFindEntityCall(c, 1, "2")
   456  	s.st.CheckCall(c, 2, "ProviderAddresses")
   457  	s.st.CheckFindEntityCall(c, 3, "3")
   458  }
   459  
   460  func (s *InstancePollerSuite) TestSetProviderAddressesSuccess(c *gc.C) {
   461  	oldAddrs := network.NewAddresses("0.1.2.3", "127.0.0.1", "8.8.8.8")
   462  	newAddrs := network.NewAddresses("1.2.3.4", "8.4.4.8", "2001:db8::")
   463  	s.st.SetMachineInfo(c, machineInfo{id: "1", providerAddresses: oldAddrs})
   464  	s.st.SetMachineInfo(c, machineInfo{id: "2", providerAddresses: nil})
   465  
   466  	result, err := s.api.SetProviderAddresses(params.SetMachinesAddresses{
   467  		MachineAddresses: []params.MachineAddresses{
   468  			{Tag: "machine-1", Addresses: nil},
   469  			{Tag: "machine-2", Addresses: params.FromNetworkAddresses(newAddrs)},
   470  			{Tag: "machine-42"},
   471  			{Tag: "service-unknown"},
   472  			{Tag: "invalid-tag"},
   473  			{Tag: "unit-missing-1"},
   474  			{Tag: ""},
   475  			{Tag: "42"},
   476  		}},
   477  	)
   478  	c.Assert(err, jc.ErrorIsNil)
   479  	c.Assert(result, jc.DeepEquals, s.mixedErrorResults)
   480  
   481  	s.st.CheckFindEntityCall(c, 0, "1")
   482  	s.st.CheckSetProviderAddressesCall(c, 1, []network.Address{})
   483  	s.st.CheckFindEntityCall(c, 2, "2")
   484  	s.st.CheckSetProviderAddressesCall(c, 3, newAddrs)
   485  	s.st.CheckFindEntityCall(c, 4, "42")
   486  
   487  	// Ensure machines were updated.
   488  	machine, err := s.st.Machine("1")
   489  	c.Assert(err, jc.ErrorIsNil)
   490  	c.Assert(machine.ProviderAddresses(), gc.HasLen, 0)
   491  
   492  	machine, err = s.st.Machine("2")
   493  	c.Assert(err, jc.ErrorIsNil)
   494  	c.Assert(machine.ProviderAddresses(), jc.DeepEquals, newAddrs)
   495  }
   496  
   497  func (s *InstancePollerSuite) TestSetProviderAddressesFailure(c *gc.C) {
   498  	s.st.SetErrors(
   499  		errors.New("pow!"),                   // m1 := FindEntity("1")
   500  		nil,                                  // m2 := FindEntity("2")
   501  		errors.New("FAIL"),                   // m2.SetProviderAddresses()
   502  		errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved)
   503  	)
   504  	oldAddrs := network.NewAddresses("0.1.2.3", "127.0.0.1", "8.8.8.8")
   505  	newAddrs := network.NewAddresses("1.2.3.4", "8.4.4.8", "2001:db8::")
   506  	s.st.SetMachineInfo(c, machineInfo{id: "1", providerAddresses: oldAddrs})
   507  	s.st.SetMachineInfo(c, machineInfo{id: "2", providerAddresses: nil})
   508  
   509  	result, err := s.api.SetProviderAddresses(params.SetMachinesAddresses{
   510  		MachineAddresses: []params.MachineAddresses{
   511  			{Tag: "machine-1", Addresses: nil},
   512  			{Tag: "machine-2", Addresses: params.FromNetworkAddresses(newAddrs)},
   513  			{Tag: "machine-3"},
   514  		}},
   515  	)
   516  	c.Assert(err, jc.ErrorIsNil)
   517  	c.Assert(result, jc.DeepEquals, s.machineErrorResults)
   518  
   519  	s.st.CheckFindEntityCall(c, 0, "1")
   520  	s.st.CheckFindEntityCall(c, 1, "2")
   521  	s.st.CheckSetProviderAddressesCall(c, 2, newAddrs)
   522  	s.st.CheckFindEntityCall(c, 3, "3")
   523  
   524  	// Ensure machine 2 wasn't updated.
   525  	machine, err := s.st.Machine("2")
   526  	c.Assert(err, jc.ErrorIsNil)
   527  	c.Assert(machine.ProviderAddresses(), gc.HasLen, 0)
   528  }
   529  
   530  func (s *InstancePollerSuite) TestInstanceStatusSuccess(c *gc.C) {
   531  	s.st.SetMachineInfo(c, machineInfo{id: "1", instanceStatus: "foo"})
   532  	s.st.SetMachineInfo(c, machineInfo{id: "2", instanceStatus: ""})
   533  
   534  	result, err := s.api.InstanceStatus(s.mixedEntities)
   535  	c.Assert(err, jc.ErrorIsNil)
   536  	c.Assert(result, jc.DeepEquals, params.StringResults{
   537  		Results: []params.StringResult{
   538  			{Result: "foo"},
   539  			{Result: ""},
   540  			{Error: apiservertesting.NotFoundError("machine 42")},
   541  			{Error: apiservertesting.ServerError(`"service-unknown" is not a valid machine tag`)},
   542  			{Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)},
   543  			{Error: apiservertesting.ServerError(`"unit-missing-1" is not a valid machine tag`)},
   544  			{Error: apiservertesting.ServerError(`"" is not a valid tag`)},
   545  			{Error: apiservertesting.ServerError(`"42" is not a valid tag`)},
   546  		}},
   547  	)
   548  
   549  	s.st.CheckFindEntityCall(c, 0, "1")
   550  	s.st.CheckCall(c, 1, "InstanceStatus")
   551  	s.st.CheckFindEntityCall(c, 2, "2")
   552  	s.st.CheckCall(c, 3, "InstanceStatus")
   553  	s.st.CheckFindEntityCall(c, 4, "42")
   554  }
   555  
   556  func (s *InstancePollerSuite) TestInstanceStatusFailure(c *gc.C) {
   557  	s.st.SetErrors(
   558  		errors.New("pow!"),                   // m1 := FindEntity("1")
   559  		nil,                                  // m2 := FindEntity("2")
   560  		errors.New("FAIL"),                   // m2.InstanceStatus()
   561  		errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved)
   562  	)
   563  	s.st.SetMachineInfo(c, machineInfo{id: "1", instanceStatus: "foo"})
   564  	s.st.SetMachineInfo(c, machineInfo{id: "2", instanceStatus: ""})
   565  
   566  	result, err := s.api.InstanceStatus(s.machineEntities)
   567  	c.Assert(err, jc.ErrorIsNil)
   568  	c.Assert(result, jc.DeepEquals, params.StringResults{
   569  		Results: []params.StringResult{
   570  			{Error: apiservertesting.ServerError("pow!")},
   571  			{Error: apiservertesting.ServerError("FAIL")},
   572  			{Error: apiservertesting.NotProvisionedError("42")},
   573  		}},
   574  	)
   575  
   576  	s.st.CheckFindEntityCall(c, 0, "1")
   577  	s.st.CheckFindEntityCall(c, 1, "2")
   578  	s.st.CheckCall(c, 2, "InstanceStatus")
   579  	s.st.CheckFindEntityCall(c, 3, "3")
   580  }
   581  
   582  func (s *InstancePollerSuite) TestSetInstanceStatusSuccess(c *gc.C) {
   583  	s.st.SetMachineInfo(c, machineInfo{id: "1", instanceStatus: "foo"})
   584  	s.st.SetMachineInfo(c, machineInfo{id: "2", instanceStatus: ""})
   585  
   586  	result, err := s.api.SetInstanceStatus(params.SetInstancesStatus{
   587  		Entities: []params.InstanceStatus{
   588  			{Tag: "machine-1", Status: ""},
   589  			{Tag: "machine-2", Status: "new status"},
   590  			{Tag: "machine-42"},
   591  			{Tag: "service-unknown"},
   592  			{Tag: "invalid-tag"},
   593  			{Tag: "unit-missing-1"},
   594  			{Tag: ""},
   595  			{Tag: "42"},
   596  		}},
   597  	)
   598  	c.Assert(err, jc.ErrorIsNil)
   599  	c.Assert(result, jc.DeepEquals, s.mixedErrorResults)
   600  
   601  	s.st.CheckFindEntityCall(c, 0, "1")
   602  	s.st.CheckCall(c, 1, "SetInstanceStatus", "")
   603  	s.st.CheckFindEntityCall(c, 2, "2")
   604  	s.st.CheckCall(c, 3, "SetInstanceStatus", "new status")
   605  	s.st.CheckFindEntityCall(c, 4, "42")
   606  
   607  	// Ensure machines were updated.
   608  	machine, err := s.st.Machine("1")
   609  	c.Assert(err, jc.ErrorIsNil)
   610  	setStatus, err := machine.InstanceStatus()
   611  	c.Assert(err, jc.ErrorIsNil)
   612  	c.Assert(setStatus, gc.Equals, "")
   613  
   614  	machine, err = s.st.Machine("2")
   615  	c.Assert(err, jc.ErrorIsNil)
   616  	setStatus, err = machine.InstanceStatus()
   617  	c.Assert(err, jc.ErrorIsNil)
   618  	c.Assert(setStatus, gc.Equals, "new status")
   619  }
   620  
   621  func (s *InstancePollerSuite) TestSetInstanceStatusFailure(c *gc.C) {
   622  	s.st.SetErrors(
   623  		errors.New("pow!"),                   // m1 := FindEntity("1")
   624  		nil,                                  // m2 := FindEntity("2")
   625  		errors.New("FAIL"),                   // m2.SetInstanceStatus()
   626  		errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved)
   627  	)
   628  	s.st.SetMachineInfo(c, machineInfo{id: "1", instanceStatus: "foo"})
   629  	s.st.SetMachineInfo(c, machineInfo{id: "2", instanceStatus: ""})
   630  
   631  	result, err := s.api.SetInstanceStatus(params.SetInstancesStatus{
   632  		Entities: []params.InstanceStatus{
   633  			{Tag: "machine-1", Status: "new"},
   634  			{Tag: "machine-2", Status: "invalid"},
   635  			{Tag: "machine-3", Status: ""},
   636  		}},
   637  	)
   638  	c.Assert(err, jc.ErrorIsNil)
   639  	c.Assert(result, jc.DeepEquals, s.machineErrorResults)
   640  
   641  	s.st.CheckFindEntityCall(c, 0, "1")
   642  	s.st.CheckFindEntityCall(c, 1, "2")
   643  	s.st.CheckCall(c, 2, "SetInstanceStatus", "invalid")
   644  	s.st.CheckFindEntityCall(c, 3, "3")
   645  }
   646  
   647  func (s *InstancePollerSuite) TestAreManuallyProvisionedSuccess(c *gc.C) {
   648  	s.st.SetMachineInfo(c, machineInfo{id: "1", isManual: true})
   649  	s.st.SetMachineInfo(c, machineInfo{id: "2", isManual: false})
   650  
   651  	result, err := s.api.AreManuallyProvisioned(s.mixedEntities)
   652  	c.Assert(err, jc.ErrorIsNil)
   653  	c.Assert(result, jc.DeepEquals, params.BoolResults{
   654  		Results: []params.BoolResult{
   655  			{Result: true},
   656  			{Result: false},
   657  			{Error: apiservertesting.NotFoundError("machine 42")},
   658  			{Error: apiservertesting.ServerError(`"service-unknown" is not a valid machine tag`)},
   659  			{Error: apiservertesting.ServerError(`"invalid-tag" is not a valid tag`)},
   660  			{Error: apiservertesting.ServerError(`"unit-missing-1" is not a valid machine tag`)},
   661  			{Error: apiservertesting.ServerError(`"" is not a valid tag`)},
   662  			{Error: apiservertesting.ServerError(`"42" is not a valid tag`)},
   663  		}},
   664  	)
   665  
   666  	s.st.CheckFindEntityCall(c, 0, "1")
   667  	s.st.CheckCall(c, 1, "IsManual")
   668  	s.st.CheckFindEntityCall(c, 2, "2")
   669  	s.st.CheckCall(c, 3, "IsManual")
   670  	s.st.CheckFindEntityCall(c, 4, "42")
   671  }
   672  
   673  func (s *InstancePollerSuite) TestAreManuallyProvisionedFailure(c *gc.C) {
   674  	s.st.SetErrors(
   675  		errors.New("pow!"),                   // m1 := FindEntity("1")
   676  		nil,                                  // m2 := FindEntity("2")
   677  		errors.New("FAIL"),                   // m2.IsManual()
   678  		errors.NotProvisionedf("machine 42"), // FindEntity("3") (ensure wrapping is preserved)
   679  	)
   680  	s.st.SetMachineInfo(c, machineInfo{id: "1", isManual: true})
   681  	s.st.SetMachineInfo(c, machineInfo{id: "2", isManual: false})
   682  
   683  	result, err := s.api.AreManuallyProvisioned(s.machineEntities)
   684  	c.Assert(err, jc.ErrorIsNil)
   685  	c.Assert(result, jc.DeepEquals, params.BoolResults{
   686  		Results: []params.BoolResult{
   687  			{Error: apiservertesting.ServerError("pow!")},
   688  			{Error: apiservertesting.ServerError("FAIL")},
   689  			{Error: apiservertesting.NotProvisionedError("42")},
   690  		}},
   691  	)
   692  
   693  	s.st.CheckFindEntityCall(c, 0, "1")
   694  	s.st.CheckFindEntityCall(c, 1, "2")
   695  	s.st.CheckCall(c, 2, "IsManual")
   696  	s.st.CheckFindEntityCall(c, 3, "3")
   697  }