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