github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/client/client_test.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package client_test
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/loggo"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/utils/series"
    17  	"github.com/juju/version"
    18  	gc "gopkg.in/check.v1"
    19  	"gopkg.in/juju/charm.v6-unstable"
    20  	"gopkg.in/juju/names.v2"
    21  
    22  	"github.com/juju/juju/agent"
    23  	"github.com/juju/juju/apiserver/client"
    24  	"github.com/juju/juju/apiserver/common"
    25  	"github.com/juju/juju/apiserver/modelconfig"
    26  	"github.com/juju/juju/apiserver/params"
    27  	"github.com/juju/juju/apiserver/testing"
    28  	"github.com/juju/juju/constraints"
    29  	"github.com/juju/juju/environs"
    30  	"github.com/juju/juju/environs/config"
    31  	"github.com/juju/juju/environs/manual"
    32  	toolstesting "github.com/juju/juju/environs/tools/testing"
    33  	"github.com/juju/juju/instance"
    34  	"github.com/juju/juju/network"
    35  	"github.com/juju/juju/permission"
    36  	"github.com/juju/juju/rpc"
    37  	"github.com/juju/juju/state"
    38  	"github.com/juju/juju/state/multiwatcher"
    39  	"github.com/juju/juju/state/presence"
    40  	"github.com/juju/juju/state/stateenvirons"
    41  	"github.com/juju/juju/status"
    42  	coretesting "github.com/juju/juju/testing"
    43  	"github.com/juju/juju/testing/factory"
    44  	jujuversion "github.com/juju/juju/version"
    45  	"github.com/juju/juju/worker"
    46  )
    47  
    48  type serverSuite struct {
    49  	baseSuite
    50  	client     *client.Client
    51  	newEnviron func() (environs.Environ, error)
    52  }
    53  
    54  var _ = gc.Suite(&serverSuite{})
    55  
    56  func (s *serverSuite) SetUpTest(c *gc.C) {
    57  	s.ConfigAttrs = map[string]interface{}{
    58  		"authorized-keys": coretesting.FakeAuthKeys,
    59  	}
    60  	s.baseSuite.SetUpTest(c)
    61  
    62  	var err error
    63  	auth := testing.FakeAuthorizer{
    64  		Tag:            s.AdminUserTag(c),
    65  		EnvironManager: true,
    66  	}
    67  	urlGetter := common.NewToolsURLGetter(s.State.ModelUUID(), s.State)
    68  	configGetter := stateenvirons.EnvironConfigGetter{s.State}
    69  	statusSetter := common.NewStatusSetter(s.State, common.AuthAlways())
    70  	toolsFinder := common.NewToolsFinder(configGetter, s.State, urlGetter)
    71  	s.newEnviron = func() (environs.Environ, error) {
    72  		return environs.GetEnviron(configGetter, environs.New)
    73  	}
    74  	newEnviron := func() (environs.Environ, error) {
    75  		return s.newEnviron()
    76  	}
    77  	blockChecker := common.NewBlockChecker(s.State)
    78  	modelConfigAPI, err := modelconfig.NewModelConfigAPI(s.State, auth)
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	s.client, err = client.NewClient(
    81  		client.NewStateBackend(s.State),
    82  		modelConfigAPI,
    83  		common.NewResources(),
    84  		auth,
    85  		statusSetter,
    86  		toolsFinder,
    87  		newEnviron,
    88  		blockChecker,
    89  	)
    90  	c.Assert(err, jc.ErrorIsNil)
    91  }
    92  
    93  func (s *serverSuite) setAgentPresence(c *gc.C, machineId string) *presence.Pinger {
    94  	m, err := s.State.Machine(machineId)
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	pinger, err := m.SetAgentPresence()
    97  	c.Assert(err, jc.ErrorIsNil)
    98  	s.AddCleanup(func(c *gc.C) {
    99  		c.Assert(worker.Stop(pinger), jc.ErrorIsNil)
   100  	})
   101  	s.State.StartSync()
   102  	err = m.WaitAgentPresence(coretesting.LongWait)
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	return pinger
   105  }
   106  
   107  func (s *serverSuite) TestModelInfo(c *gc.C) {
   108  	model, err := s.State.Model()
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	conf, _ := s.State.ModelConfig()
   111  	info, err := s.client.ModelInfo()
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	c.Assert(info.DefaultSeries, gc.Equals, config.PreferredSeries(conf))
   114  	c.Assert(info.CloudRegion, gc.Equals, model.CloudRegion())
   115  	c.Assert(info.ProviderType, gc.Equals, conf.Type())
   116  	c.Assert(info.Name, gc.Equals, conf.Name())
   117  	c.Assert(info.UUID, gc.Equals, model.UUID())
   118  	c.Assert(info.OwnerTag, gc.Equals, model.Owner().String())
   119  	c.Assert(info.Life, gc.Equals, params.Alive)
   120  	// The controller UUID is not returned by the ModelInfo endpoint on the
   121  	// Client facade.
   122  	c.Assert(info.ControllerUUID, gc.Equals, "")
   123  }
   124  
   125  func (s *serverSuite) TestModelUsersInfo(c *gc.C) {
   126  	testAdmin := s.AdminUserTag(c)
   127  	owner, err := s.State.UserAccess(testAdmin, s.State.ModelTag())
   128  	c.Assert(err, jc.ErrorIsNil)
   129  
   130  	localUser1 := s.makeLocalModelUser(c, "ralphdoe", "Ralph Doe")
   131  	localUser2 := s.makeLocalModelUser(c, "samsmith", "Sam Smith")
   132  	remoteUser1 := s.Factory.MakeModelUser(c, &factory.ModelUserParams{User: "bobjohns@ubuntuone", DisplayName: "Bob Johns", Access: permission.WriteAccess})
   133  	remoteUser2 := s.Factory.MakeModelUser(c, &factory.ModelUserParams{User: "nicshaw@idprovider", DisplayName: "Nic Shaw", Access: permission.WriteAccess})
   134  
   135  	results, err := s.client.ModelUserInfo()
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	var expected params.ModelUserInfoResults
   138  	for _, r := range []struct {
   139  		user permission.UserAccess
   140  		info *params.ModelUserInfo
   141  	}{
   142  		{
   143  			owner,
   144  			&params.ModelUserInfo{
   145  				UserName:    owner.UserName,
   146  				DisplayName: owner.DisplayName,
   147  				Access:      "admin",
   148  			},
   149  		}, {
   150  			localUser1,
   151  			&params.ModelUserInfo{
   152  				UserName:    "ralphdoe",
   153  				DisplayName: "Ralph Doe",
   154  				Access:      "admin",
   155  			},
   156  		}, {
   157  			localUser2,
   158  			&params.ModelUserInfo{
   159  				UserName:    "samsmith",
   160  				DisplayName: "Sam Smith",
   161  				Access:      "admin",
   162  			},
   163  		}, {
   164  			remoteUser1,
   165  			&params.ModelUserInfo{
   166  				UserName:    "bobjohns@ubuntuone",
   167  				DisplayName: "Bob Johns",
   168  				Access:      "write",
   169  			},
   170  		}, {
   171  			remoteUser2,
   172  			&params.ModelUserInfo{
   173  				UserName:    "nicshaw@idprovider",
   174  				DisplayName: "Nic Shaw",
   175  				Access:      "write",
   176  			},
   177  		},
   178  	} {
   179  		r.info.LastConnection = lastConnPointer(c, r.user, s.State)
   180  		expected.Results = append(expected.Results, params.ModelUserInfoResult{Result: r.info})
   181  	}
   182  
   183  	sort.Sort(ByUserName(expected.Results))
   184  	sort.Sort(ByUserName(results.Results))
   185  	c.Assert(results, jc.DeepEquals, expected)
   186  }
   187  
   188  func lastConnPointer(c *gc.C, modelUser permission.UserAccess, st *state.State) *time.Time {
   189  	lastConn, err := st.LastModelConnection(modelUser.UserTag)
   190  	if err != nil {
   191  		if state.IsNeverConnectedError(err) {
   192  			return nil
   193  		}
   194  		c.Fatal(err)
   195  	}
   196  	return &lastConn
   197  }
   198  
   199  // ByUserName implements sort.Interface for []params.ModelUserInfoResult based on
   200  // the UserName field.
   201  type ByUserName []params.ModelUserInfoResult
   202  
   203  func (a ByUserName) Len() int           { return len(a) }
   204  func (a ByUserName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   205  func (a ByUserName) Less(i, j int) bool { return a[i].Result.UserName < a[j].Result.UserName }
   206  
   207  func (s *serverSuite) makeLocalModelUser(c *gc.C, username, displayname string) permission.UserAccess {
   208  	// factory.MakeUser will create an ModelUser for a local user by defalut
   209  	user := s.Factory.MakeUser(c, &factory.UserParams{Name: username, DisplayName: displayname})
   210  	modelUser, err := s.State.UserAccess(user.UserTag(), s.State.ModelTag())
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	return modelUser
   213  }
   214  
   215  func (s *serverSuite) TestSetEnvironAgentVersion(c *gc.C) {
   216  	args := params.SetModelAgentVersion{
   217  		Version: version.MustParse("9.8.7"),
   218  	}
   219  	err := s.client.SetModelAgentVersion(args)
   220  	c.Assert(err, jc.ErrorIsNil)
   221  
   222  	modelConfig, err := s.State.ModelConfig()
   223  	c.Assert(err, jc.ErrorIsNil)
   224  	agentVersion, found := modelConfig.AllAttrs()["agent-version"]
   225  	c.Assert(found, jc.IsTrue)
   226  	c.Assert(agentVersion, gc.Equals, "9.8.7")
   227  }
   228  
   229  type mockEnviron struct {
   230  	environs.Environ
   231  	allInstancesCalled bool
   232  	err                error
   233  }
   234  
   235  func (m *mockEnviron) AllInstances() ([]instance.Instance, error) {
   236  	m.allInstancesCalled = true
   237  	return nil, m.err
   238  }
   239  
   240  func (s *serverSuite) assertCheckProviderAPI(c *gc.C, envError error, expectErr string) {
   241  	env := &mockEnviron{err: envError}
   242  	s.newEnviron = func() (environs.Environ, error) {
   243  		return env, nil
   244  	}
   245  	args := params.SetModelAgentVersion{
   246  		Version: version.MustParse("9.8.7"),
   247  	}
   248  	err := s.client.SetModelAgentVersion(args)
   249  	c.Assert(env.allInstancesCalled, jc.IsTrue)
   250  	if expectErr != "" {
   251  		c.Assert(err, gc.ErrorMatches, expectErr)
   252  	} else {
   253  		c.Assert(err, jc.ErrorIsNil)
   254  	}
   255  }
   256  
   257  func (s *serverSuite) TestCheckProviderAPISuccess(c *gc.C) {
   258  	s.assertCheckProviderAPI(c, nil, "")
   259  	s.assertCheckProviderAPI(c, environs.ErrPartialInstances, "")
   260  	s.assertCheckProviderAPI(c, environs.ErrNoInstances, "")
   261  }
   262  
   263  func (s *serverSuite) TestCheckProviderAPIFail(c *gc.C) {
   264  	s.assertCheckProviderAPI(c, errors.New("instances error"), "cannot make API call to provider: instances error")
   265  }
   266  
   267  func (s *serverSuite) assertSetEnvironAgentVersion(c *gc.C) {
   268  	args := params.SetModelAgentVersion{
   269  		Version: version.MustParse("9.8.7"),
   270  	}
   271  	err := s.client.SetModelAgentVersion(args)
   272  	c.Assert(err, jc.ErrorIsNil)
   273  	modelConfig, err := s.State.ModelConfig()
   274  	c.Assert(err, jc.ErrorIsNil)
   275  	agentVersion, found := modelConfig.AllAttrs()["agent-version"]
   276  	c.Assert(found, jc.IsTrue)
   277  	c.Assert(agentVersion, gc.Equals, "9.8.7")
   278  }
   279  
   280  func (s *serverSuite) assertSetEnvironAgentVersionBlocked(c *gc.C, msg string) {
   281  	args := params.SetModelAgentVersion{
   282  		Version: version.MustParse("9.8.7"),
   283  	}
   284  	err := s.client.SetModelAgentVersion(args)
   285  	s.AssertBlocked(c, err, msg)
   286  }
   287  
   288  func (s *serverSuite) TestBlockDestroySetEnvironAgentVersion(c *gc.C) {
   289  	s.BlockDestroyModel(c, "TestBlockDestroySetEnvironAgentVersion")
   290  	s.assertSetEnvironAgentVersion(c)
   291  }
   292  
   293  func (s *serverSuite) TestBlockRemoveSetEnvironAgentVersion(c *gc.C) {
   294  	s.BlockRemoveObject(c, "TestBlockRemoveSetEnvironAgentVersion")
   295  	s.assertSetEnvironAgentVersion(c)
   296  }
   297  
   298  func (s *serverSuite) TestBlockChangesSetEnvironAgentVersion(c *gc.C) {
   299  	s.BlockAllChanges(c, "TestBlockChangesSetEnvironAgentVersion")
   300  	s.assertSetEnvironAgentVersionBlocked(c, "TestBlockChangesSetEnvironAgentVersion")
   301  }
   302  
   303  func (s *serverSuite) TestAbortCurrentUpgrade(c *gc.C) {
   304  	// Create a provisioned controller.
   305  	machine, err := s.State.AddMachine("series", state.JobManageModel)
   306  	c.Assert(err, jc.ErrorIsNil)
   307  	err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil)
   308  	c.Assert(err, jc.ErrorIsNil)
   309  
   310  	// Start an upgrade.
   311  	_, err = s.State.EnsureUpgradeInfo(
   312  		machine.Id(),
   313  		version.MustParse("1.2.3"),
   314  		version.MustParse("9.8.7"),
   315  	)
   316  	c.Assert(err, jc.ErrorIsNil)
   317  	isUpgrading, err := s.State.IsUpgrading()
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	c.Assert(isUpgrading, jc.IsTrue)
   320  
   321  	// Abort it.
   322  	err = s.client.AbortCurrentUpgrade()
   323  	c.Assert(err, jc.ErrorIsNil)
   324  
   325  	isUpgrading, err = s.State.IsUpgrading()
   326  	c.Assert(err, jc.ErrorIsNil)
   327  	c.Assert(isUpgrading, jc.IsFalse)
   328  }
   329  
   330  func (s *serverSuite) assertAbortCurrentUpgradeBlocked(c *gc.C, msg string) {
   331  	err := s.client.AbortCurrentUpgrade()
   332  	s.AssertBlocked(c, err, msg)
   333  }
   334  
   335  func (s *serverSuite) assertAbortCurrentUpgrade(c *gc.C) {
   336  	err := s.client.AbortCurrentUpgrade()
   337  	c.Assert(err, jc.ErrorIsNil)
   338  	isUpgrading, err := s.State.IsUpgrading()
   339  	c.Assert(err, jc.ErrorIsNil)
   340  	c.Assert(isUpgrading, jc.IsFalse)
   341  }
   342  
   343  func (s *serverSuite) setupAbortCurrentUpgradeBlocked(c *gc.C) {
   344  	// Create a provisioned controller.
   345  	machine, err := s.State.AddMachine("series", state.JobManageModel)
   346  	c.Assert(err, jc.ErrorIsNil)
   347  	err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil)
   348  	c.Assert(err, jc.ErrorIsNil)
   349  
   350  	// Start an upgrade.
   351  	_, err = s.State.EnsureUpgradeInfo(
   352  		machine.Id(),
   353  		version.MustParse("1.2.3"),
   354  		version.MustParse("9.8.7"),
   355  	)
   356  	c.Assert(err, jc.ErrorIsNil)
   357  	isUpgrading, err := s.State.IsUpgrading()
   358  	c.Assert(err, jc.ErrorIsNil)
   359  	c.Assert(isUpgrading, jc.IsTrue)
   360  }
   361  
   362  func (s *serverSuite) TestBlockDestroyAbortCurrentUpgrade(c *gc.C) {
   363  	s.setupAbortCurrentUpgradeBlocked(c)
   364  	s.BlockDestroyModel(c, "TestBlockDestroyAbortCurrentUpgrade")
   365  	s.assertAbortCurrentUpgrade(c)
   366  }
   367  
   368  func (s *serverSuite) TestBlockRemoveAbortCurrentUpgrade(c *gc.C) {
   369  	s.setupAbortCurrentUpgradeBlocked(c)
   370  	s.BlockRemoveObject(c, "TestBlockRemoveAbortCurrentUpgrade")
   371  	s.assertAbortCurrentUpgrade(c)
   372  }
   373  
   374  func (s *serverSuite) TestBlockChangesAbortCurrentUpgrade(c *gc.C) {
   375  	s.setupAbortCurrentUpgradeBlocked(c)
   376  	s.BlockAllChanges(c, "TestBlockChangesAbortCurrentUpgrade")
   377  	s.assertAbortCurrentUpgradeBlocked(c, "TestBlockChangesAbortCurrentUpgrade")
   378  }
   379  
   380  type clientSuite struct {
   381  	baseSuite
   382  }
   383  
   384  var _ = gc.Suite(&clientSuite{})
   385  
   386  // clearSinceTimes zeros out the updated timestamps inside status
   387  // so we can easily check the results.
   388  func clearSinceTimes(status *params.FullStatus) {
   389  	for applicationId, service := range status.Applications {
   390  		for unitId, unit := range service.Units {
   391  			unit.WorkloadStatus.Since = nil
   392  			unit.AgentStatus.Since = nil
   393  			for id, subord := range unit.Subordinates {
   394  				subord.WorkloadStatus.Since = nil
   395  				subord.AgentStatus.Since = nil
   396  				unit.Subordinates[id] = subord
   397  			}
   398  			service.Units[unitId] = unit
   399  		}
   400  		service.Status.Since = nil
   401  		status.Applications[applicationId] = service
   402  	}
   403  	for id, machine := range status.Machines {
   404  		machine.AgentStatus.Since = nil
   405  		machine.InstanceStatus.Since = nil
   406  		status.Machines[id] = machine
   407  	}
   408  }
   409  
   410  func (s *clientSuite) TestClientStatus(c *gc.C) {
   411  	s.setUpScenario(c)
   412  	status, err := s.APIState.Client().Status(nil)
   413  	clearSinceTimes(status)
   414  	c.Assert(err, jc.ErrorIsNil)
   415  	c.Assert(status, jc.DeepEquals, scenarioStatus)
   416  }
   417  
   418  func assertLife(c *gc.C, entity state.Living, life state.Life) {
   419  	err := entity.Refresh()
   420  	c.Assert(err, jc.ErrorIsNil)
   421  	c.Assert(entity.Life(), gc.Equals, life)
   422  }
   423  
   424  func assertRemoved(c *gc.C, entity state.Living) {
   425  	err := entity.Refresh()
   426  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   427  }
   428  
   429  func (s *clientSuite) setupDestroyMachinesTest(c *gc.C) (*state.Machine, *state.Machine, *state.Machine, *state.Unit) {
   430  	m0, err := s.State.AddMachine("quantal", state.JobManageModel)
   431  	c.Assert(err, jc.ErrorIsNil)
   432  	m1, err := s.State.AddMachine("quantal", state.JobHostUnits)
   433  	c.Assert(err, jc.ErrorIsNil)
   434  	m2, err := s.State.AddMachine("quantal", state.JobHostUnits)
   435  	c.Assert(err, jc.ErrorIsNil)
   436  
   437  	sch := s.AddTestingCharm(c, "wordpress")
   438  	wordpress := s.AddTestingService(c, "wordpress", sch)
   439  	u, err := wordpress.AddUnit()
   440  	c.Assert(err, jc.ErrorIsNil)
   441  	err = u.AssignToMachine(m1)
   442  	c.Assert(err, jc.ErrorIsNil)
   443  
   444  	return m0, m1, m2, u
   445  }
   446  
   447  func (s *clientSuite) TestDestroyMachines(c *gc.C) {
   448  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
   449  	s.assertDestroyMachineSuccess(c, u, m0, m1, m2)
   450  }
   451  
   452  func (s *clientSuite) TestForceDestroyMachines(c *gc.C) {
   453  	s.assertForceDestroyMachines(c)
   454  }
   455  
   456  func (s *clientSuite) testClientUnitResolved(c *gc.C, noretry bool, expectedResolvedMode state.ResolvedMode) {
   457  	// Setup:
   458  	s.setUpScenario(c)
   459  	u, err := s.State.Unit("wordpress/0")
   460  	c.Assert(err, jc.ErrorIsNil)
   461  	now := time.Now()
   462  	sInfo := status.StatusInfo{
   463  		Status:  status.Error,
   464  		Message: "gaaah",
   465  		Since:   &now,
   466  	}
   467  	err = u.SetAgentStatus(sInfo)
   468  	c.Assert(err, jc.ErrorIsNil)
   469  	// Code under test:
   470  	err = s.APIState.Client().Resolved("wordpress/0", noretry)
   471  	c.Assert(err, jc.ErrorIsNil)
   472  	// Freshen the unit's state.
   473  	err = u.Refresh()
   474  	c.Assert(err, jc.ErrorIsNil)
   475  	// And now the actual test assertions: we set the unit as resolved via
   476  	// the API so it should have a resolved mode set.
   477  	mode := u.Resolved()
   478  	c.Assert(mode, gc.Equals, expectedResolvedMode)
   479  }
   480  
   481  func (s *clientSuite) TestClientUnitResolved(c *gc.C) {
   482  	s.testClientUnitResolved(c, true, state.ResolvedNoHooks)
   483  }
   484  
   485  func (s *clientSuite) TestClientUnitResolvedRetry(c *gc.C) {
   486  	s.testClientUnitResolved(c, false, state.ResolvedRetryHooks)
   487  }
   488  
   489  func (s *clientSuite) setupResolved(c *gc.C) *state.Unit {
   490  	s.setUpScenario(c)
   491  	u, err := s.State.Unit("wordpress/0")
   492  	c.Assert(err, jc.ErrorIsNil)
   493  	now := time.Now()
   494  	sInfo := status.StatusInfo{
   495  		Status:  status.Error,
   496  		Message: "gaaah",
   497  		Since:   &now,
   498  	}
   499  	err = u.SetAgentStatus(sInfo)
   500  	c.Assert(err, jc.ErrorIsNil)
   501  	return u
   502  }
   503  
   504  func (s *clientSuite) assertResolved(c *gc.C, u *state.Unit) {
   505  	err := s.APIState.Client().Resolved("wordpress/0", false)
   506  	c.Assert(err, jc.ErrorIsNil)
   507  	// Freshen the unit's state.
   508  	err = u.Refresh()
   509  	c.Assert(err, jc.ErrorIsNil)
   510  	// And now the actual test assertions: we set the unit as resolved via
   511  	// the API so it should have a resolved mode set.
   512  	mode := u.Resolved()
   513  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
   514  }
   515  
   516  func (s *clientSuite) assertResolvedBlocked(c *gc.C, u *state.Unit, msg string) {
   517  	err := s.APIState.Client().Resolved("wordpress/0", false)
   518  	s.AssertBlocked(c, err, msg)
   519  }
   520  
   521  func (s *clientSuite) TestBlockDestroyUnitResolved(c *gc.C) {
   522  	u := s.setupResolved(c)
   523  	s.BlockDestroyModel(c, "TestBlockDestroyUnitResolved")
   524  	s.assertResolved(c, u)
   525  }
   526  
   527  func (s *clientSuite) TestBlockRemoveUnitResolved(c *gc.C) {
   528  	u := s.setupResolved(c)
   529  	s.BlockRemoveObject(c, "TestBlockRemoveUnitResolved")
   530  	s.assertResolved(c, u)
   531  }
   532  
   533  func (s *clientSuite) TestBlockChangeUnitResolved(c *gc.C) {
   534  	u := s.setupResolved(c)
   535  	s.BlockAllChanges(c, "TestBlockChangeUnitResolved")
   536  	s.assertResolvedBlocked(c, u, "TestBlockChangeUnitResolved")
   537  }
   538  
   539  type clientRepoSuite struct {
   540  	baseSuite
   541  	testing.CharmStoreSuite
   542  }
   543  
   544  var _ = gc.Suite(&clientRepoSuite{})
   545  
   546  func (s *clientRepoSuite) SetUpSuite(c *gc.C) {
   547  	s.CharmStoreSuite.SetUpSuite(c)
   548  	s.baseSuite.SetUpSuite(c)
   549  
   550  }
   551  
   552  func (s *clientRepoSuite) TearDownSuite(c *gc.C) {
   553  	s.CharmStoreSuite.TearDownSuite(c)
   554  	s.baseSuite.TearDownSuite(c)
   555  }
   556  
   557  func (s *clientRepoSuite) SetUpTest(c *gc.C) {
   558  	s.baseSuite.SetUpTest(c)
   559  	s.CharmStoreSuite.Session = s.baseSuite.Session
   560  	s.CharmStoreSuite.SetUpTest(c)
   561  
   562  	c.Assert(s.APIState, gc.NotNil)
   563  }
   564  
   565  func (s *clientRepoSuite) TearDownTest(c *gc.C) {
   566  	s.CharmStoreSuite.TearDownTest(c)
   567  	s.baseSuite.TearDownTest(c)
   568  }
   569  
   570  func (s *clientSuite) TestClientWatchAll(c *gc.C) {
   571  	loggo.GetLogger("juju.apiserver").SetLogLevel(loggo.TRACE)
   572  	// A very simple end-to-end test, because
   573  	// all the logic is tested elsewhere.
   574  	m, err := s.State.AddMachine("quantal", state.JobManageModel)
   575  	c.Assert(err, jc.ErrorIsNil)
   576  	err = m.SetProvisioned("i-0", agent.BootstrapNonce, nil)
   577  	c.Assert(err, jc.ErrorIsNil)
   578  	watcher, err := s.APIState.Client().WatchAll()
   579  	c.Assert(err, jc.ErrorIsNil)
   580  	defer func() {
   581  		err := watcher.Stop()
   582  		c.Assert(err, jc.ErrorIsNil)
   583  	}()
   584  	deltas, err := watcher.Next()
   585  	c.Assert(err, jc.ErrorIsNil)
   586  	c.Assert(len(deltas), gc.Equals, 1)
   587  	d0, ok := deltas[0].Entity.(*multiwatcher.MachineInfo)
   588  	c.Assert(ok, jc.IsTrue)
   589  	d0.AgentStatus.Since = nil
   590  	d0.InstanceStatus.Since = nil
   591  	if !c.Check(deltas, jc.DeepEquals, []multiwatcher.Delta{{
   592  		Entity: &multiwatcher.MachineInfo{
   593  			ModelUUID:  s.State.ModelUUID(),
   594  			Id:         m.Id(),
   595  			InstanceId: "i-0",
   596  			AgentStatus: multiwatcher.StatusInfo{
   597  				Current: status.Pending,
   598  			},
   599  			InstanceStatus: multiwatcher.StatusInfo{
   600  				Current: status.Pending,
   601  			},
   602  			Life:                    multiwatcher.Life("alive"),
   603  			Series:                  "quantal",
   604  			Jobs:                    []multiwatcher.MachineJob{state.JobManageModel.ToParams()},
   605  			Addresses:               []multiwatcher.Address{},
   606  			HardwareCharacteristics: &instance.HardwareCharacteristics{},
   607  			HasVote:                 false,
   608  			WantsVote:               true,
   609  		},
   610  	}}) {
   611  		c.Logf("got:")
   612  		for _, d := range deltas {
   613  			c.Logf("%#v\n", d.Entity)
   614  		}
   615  	}
   616  }
   617  
   618  func (s *clientSuite) TestClientSetModelConstraints(c *gc.C) {
   619  	// Set constraints for the model.
   620  	cons, err := constraints.Parse("mem=4096", "cores=2")
   621  	c.Assert(err, jc.ErrorIsNil)
   622  	err = s.APIState.Client().SetModelConstraints(cons)
   623  	c.Assert(err, jc.ErrorIsNil)
   624  
   625  	// Ensure the constraints have been correctly updated.
   626  	obtained, err := s.State.ModelConstraints()
   627  	c.Assert(err, jc.ErrorIsNil)
   628  	c.Assert(obtained, gc.DeepEquals, cons)
   629  }
   630  
   631  func (s *clientSuite) assertSetModelConstraints(c *gc.C) {
   632  	// Set constraints for the model.
   633  	cons, err := constraints.Parse("mem=4096", "cores=2")
   634  	c.Assert(err, jc.ErrorIsNil)
   635  	err = s.APIState.Client().SetModelConstraints(cons)
   636  	c.Assert(err, jc.ErrorIsNil)
   637  	// Ensure the constraints have been correctly updated.
   638  	obtained, err := s.State.ModelConstraints()
   639  	c.Assert(err, jc.ErrorIsNil)
   640  	c.Assert(obtained, gc.DeepEquals, cons)
   641  }
   642  
   643  func (s *clientSuite) assertSetModelConstraintsBlocked(c *gc.C, msg string) {
   644  	// Set constraints for the model.
   645  	cons, err := constraints.Parse("mem=4096", "cores=2")
   646  	c.Assert(err, jc.ErrorIsNil)
   647  	err = s.APIState.Client().SetModelConstraints(cons)
   648  	s.AssertBlocked(c, err, msg)
   649  }
   650  
   651  func (s *clientSuite) TestBlockDestroyClientSetModelConstraints(c *gc.C) {
   652  	s.BlockDestroyModel(c, "TestBlockDestroyClientSetModelConstraints")
   653  	s.assertSetModelConstraints(c)
   654  }
   655  
   656  func (s *clientSuite) TestBlockRemoveClientSetModelConstraints(c *gc.C) {
   657  	s.BlockRemoveObject(c, "TestBlockRemoveClientSetModelConstraints")
   658  	s.assertSetModelConstraints(c)
   659  }
   660  
   661  func (s *clientSuite) TestBlockChangesClientSetModelConstraints(c *gc.C) {
   662  	s.BlockAllChanges(c, "TestBlockChangesClientSetModelConstraints")
   663  	s.assertSetModelConstraintsBlocked(c, "TestBlockChangesClientSetModelConstraints")
   664  }
   665  
   666  func (s *clientSuite) TestClientGetModelConstraints(c *gc.C) {
   667  	// Set constraints for the model.
   668  	cons, err := constraints.Parse("mem=4096", "cores=2")
   669  	c.Assert(err, jc.ErrorIsNil)
   670  	err = s.State.SetModelConstraints(cons)
   671  	c.Assert(err, jc.ErrorIsNil)
   672  
   673  	// Check we can get the constraints.
   674  	obtained, err := s.APIState.Client().GetModelConstraints()
   675  	c.Assert(err, jc.ErrorIsNil)
   676  	c.Assert(obtained, gc.DeepEquals, cons)
   677  }
   678  
   679  func (s *clientSuite) TestClientPublicAddressErrors(c *gc.C) {
   680  	s.setUpScenario(c)
   681  	_, err := s.APIState.Client().PublicAddress("wordpress")
   682  	c.Assert(err, gc.ErrorMatches, `unknown unit or machine "wordpress"`)
   683  	_, err = s.APIState.Client().PublicAddress("0")
   684  	c.Assert(err, gc.ErrorMatches, `error fetching address for machine "0": no public address`)
   685  	_, err = s.APIState.Client().PublicAddress("wordpress/0")
   686  	c.Assert(err, gc.ErrorMatches, `error fetching address for unit "wordpress/0": no public address`)
   687  }
   688  
   689  func (s *clientSuite) TestClientPublicAddressMachine(c *gc.C) {
   690  	s.setUpScenario(c)
   691  
   692  	// Internally, network.SelectPublicAddress is used; the "most public"
   693  	// address is returned.
   694  	m1, err := s.State.Machine("1")
   695  	c.Assert(err, jc.ErrorIsNil)
   696  	cloudLocalAddress := network.NewScopedAddress("cloudlocal", network.ScopeCloudLocal)
   697  	publicAddress := network.NewScopedAddress("public", network.ScopePublic)
   698  	err = m1.SetProviderAddresses(cloudLocalAddress)
   699  	c.Assert(err, jc.ErrorIsNil)
   700  	addr, err := s.APIState.Client().PublicAddress("1")
   701  	c.Assert(err, jc.ErrorIsNil)
   702  	c.Assert(addr, gc.Equals, "cloudlocal")
   703  	err = m1.SetProviderAddresses(cloudLocalAddress, publicAddress)
   704  	addr, err = s.APIState.Client().PublicAddress("1")
   705  	c.Assert(err, jc.ErrorIsNil)
   706  	c.Assert(addr, gc.Equals, "public")
   707  }
   708  
   709  func (s *clientSuite) TestClientPublicAddressUnit(c *gc.C) {
   710  	s.setUpScenario(c)
   711  
   712  	m1, err := s.State.Machine("1")
   713  	publicAddress := network.NewScopedAddress("public", network.ScopePublic)
   714  	err = m1.SetProviderAddresses(publicAddress)
   715  	c.Assert(err, jc.ErrorIsNil)
   716  	addr, err := s.APIState.Client().PublicAddress("wordpress/0")
   717  	c.Assert(err, jc.ErrorIsNil)
   718  	c.Assert(addr, gc.Equals, "public")
   719  }
   720  
   721  func (s *clientSuite) TestClientPrivateAddressErrors(c *gc.C) {
   722  	s.setUpScenario(c)
   723  	_, err := s.APIState.Client().PrivateAddress("wordpress")
   724  	c.Assert(err, gc.ErrorMatches, `unknown unit or machine "wordpress"`)
   725  	_, err = s.APIState.Client().PrivateAddress("0")
   726  	c.Assert(err, gc.ErrorMatches, `error fetching address for machine "0": no private address`)
   727  	_, err = s.APIState.Client().PrivateAddress("wordpress/0")
   728  	c.Assert(err, gc.ErrorMatches, `error fetching address for unit "wordpress/0": no private address`)
   729  }
   730  
   731  func (s *clientSuite) TestClientPrivateAddress(c *gc.C) {
   732  	s.setUpScenario(c)
   733  
   734  	// Internally, network.SelectInternalAddress is used; the public
   735  	// address if no cloud-local one is available.
   736  	m1, err := s.State.Machine("1")
   737  	c.Assert(err, jc.ErrorIsNil)
   738  	cloudLocalAddress := network.NewScopedAddress("cloudlocal", network.ScopeCloudLocal)
   739  	publicAddress := network.NewScopedAddress("public", network.ScopePublic)
   740  	err = m1.SetProviderAddresses(publicAddress)
   741  	c.Assert(err, jc.ErrorIsNil)
   742  	addr, err := s.APIState.Client().PrivateAddress("1")
   743  	c.Assert(err, jc.ErrorIsNil)
   744  	c.Assert(addr, gc.Equals, "public")
   745  	err = m1.SetProviderAddresses(cloudLocalAddress, publicAddress)
   746  	addr, err = s.APIState.Client().PrivateAddress("1")
   747  	c.Assert(err, jc.ErrorIsNil)
   748  	c.Assert(addr, gc.Equals, "cloudlocal")
   749  }
   750  
   751  func (s *clientSuite) TestClientPrivateAddressUnit(c *gc.C) {
   752  	s.setUpScenario(c)
   753  
   754  	m1, err := s.State.Machine("1")
   755  	privateAddress := network.NewScopedAddress("private", network.ScopeCloudLocal)
   756  	err = m1.SetProviderAddresses(privateAddress)
   757  	c.Assert(err, jc.ErrorIsNil)
   758  	addr, err := s.APIState.Client().PrivateAddress("wordpress/0")
   759  	c.Assert(err, jc.ErrorIsNil)
   760  	c.Assert(addr, gc.Equals, "private")
   761  }
   762  
   763  func (s *clientSuite) TestClientFindTools(c *gc.C) {
   764  	result, err := s.APIState.Client().FindTools(99, -1, "", "")
   765  	c.Assert(err, jc.ErrorIsNil)
   766  	c.Assert(result.Error, jc.Satisfies, params.IsCodeNotFound)
   767  	toolstesting.UploadToStorage(c, s.DefaultToolsStorage, "released", version.MustParseBinary("2.99.0-precise-amd64"))
   768  	result, err = s.APIState.Client().FindTools(2, 99, "precise", "amd64")
   769  	c.Assert(err, jc.ErrorIsNil)
   770  	c.Assert(result.Error, gc.IsNil)
   771  	c.Assert(result.List, gc.HasLen, 1)
   772  	c.Assert(result.List[0].Version, gc.Equals, version.MustParseBinary("2.99.0-precise-amd64"))
   773  	url := fmt.Sprintf("https://%s/model/%s/tools/%s",
   774  		s.APIState.Addr(), coretesting.ModelTag.Id(), result.List[0].Version)
   775  	c.Assert(result.List[0].URL, gc.Equals, url)
   776  }
   777  
   778  func (s *clientSuite) checkMachine(c *gc.C, id, series, cons string) {
   779  	// Ensure the machine was actually created.
   780  	machine, err := s.BackingState.Machine(id)
   781  	c.Assert(err, jc.ErrorIsNil)
   782  	c.Assert(machine.Series(), gc.Equals, series)
   783  	c.Assert(machine.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobHostUnits})
   784  	machineConstraints, err := machine.Constraints()
   785  	c.Assert(err, jc.ErrorIsNil)
   786  	c.Assert(machineConstraints.String(), gc.Equals, cons)
   787  }
   788  
   789  func (s *clientSuite) TestClientAddMachinesDefaultSeries(c *gc.C) {
   790  	apiParams := make([]params.AddMachineParams, 3)
   791  	for i := 0; i < 3; i++ {
   792  		apiParams[i] = params.AddMachineParams{
   793  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   794  		}
   795  	}
   796  	machines, err := s.APIState.Client().AddMachines(apiParams)
   797  	c.Assert(err, jc.ErrorIsNil)
   798  	c.Assert(len(machines), gc.Equals, 3)
   799  	for i, machineResult := range machines {
   800  		c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
   801  		s.checkMachine(c, machineResult.Machine, series.LatestLts(), apiParams[i].Constraints.String())
   802  	}
   803  }
   804  
   805  func (s *clientSuite) assertAddMachines(c *gc.C) {
   806  	apiParams := make([]params.AddMachineParams, 3)
   807  	for i := 0; i < 3; i++ {
   808  		apiParams[i] = params.AddMachineParams{
   809  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   810  		}
   811  	}
   812  	machines, err := s.APIState.Client().AddMachines(apiParams)
   813  	c.Assert(err, jc.ErrorIsNil)
   814  	c.Assert(len(machines), gc.Equals, 3)
   815  	for i, machineResult := range machines {
   816  		c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
   817  		s.checkMachine(c, machineResult.Machine, series.LatestLts(), apiParams[i].Constraints.String())
   818  	}
   819  }
   820  
   821  func (s *clientSuite) assertAddMachinesBlocked(c *gc.C, msg string) {
   822  	apiParams := make([]params.AddMachineParams, 3)
   823  	for i := 0; i < 3; i++ {
   824  		apiParams[i] = params.AddMachineParams{
   825  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   826  		}
   827  	}
   828  	_, err := s.APIState.Client().AddMachines(apiParams)
   829  	s.AssertBlocked(c, err, msg)
   830  }
   831  
   832  func (s *clientSuite) TestBlockDestroyClientAddMachinesDefaultSeries(c *gc.C) {
   833  	s.BlockDestroyModel(c, "TestBlockDestroyClientAddMachinesDefaultSeries")
   834  	s.assertAddMachines(c)
   835  }
   836  
   837  func (s *clientSuite) TestBlockRemoveClientAddMachinesDefaultSeries(c *gc.C) {
   838  	s.BlockRemoveObject(c, "TestBlockRemoveClientAddMachinesDefaultSeries")
   839  	s.assertAddMachines(c)
   840  }
   841  
   842  func (s *clientSuite) TestBlockChangesClientAddMachines(c *gc.C) {
   843  	s.BlockAllChanges(c, "TestBlockChangesClientAddMachines")
   844  	s.assertAddMachinesBlocked(c, "TestBlockChangesClientAddMachines")
   845  }
   846  
   847  func (s *clientSuite) TestClientAddMachinesWithSeries(c *gc.C) {
   848  	apiParams := make([]params.AddMachineParams, 3)
   849  	for i := 0; i < 3; i++ {
   850  		apiParams[i] = params.AddMachineParams{
   851  			Series: "quantal",
   852  			Jobs:   []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   853  		}
   854  	}
   855  	machines, err := s.APIState.Client().AddMachines(apiParams)
   856  	c.Assert(err, jc.ErrorIsNil)
   857  	c.Assert(len(machines), gc.Equals, 3)
   858  	for i, machineResult := range machines {
   859  		c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
   860  		s.checkMachine(c, machineResult.Machine, "quantal", apiParams[i].Constraints.String())
   861  	}
   862  }
   863  
   864  func (s *clientSuite) TestClientAddMachineInsideMachine(c *gc.C) {
   865  	_, err := s.State.AddMachine("quantal", state.JobHostUnits)
   866  	c.Assert(err, jc.ErrorIsNil)
   867  
   868  	machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{{
   869  		Jobs:          []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   870  		ContainerType: instance.LXD,
   871  		ParentId:      "0",
   872  		Series:        "quantal",
   873  	}})
   874  	c.Assert(err, jc.ErrorIsNil)
   875  	c.Assert(machines, gc.HasLen, 1)
   876  	c.Assert(machines[0].Machine, gc.Equals, "0/lxd/0")
   877  }
   878  
   879  // updateConfig sets config variable with given key to a given value
   880  // Asserts that no errors were encountered.
   881  func (s *baseSuite) updateConfig(c *gc.C, key string, block bool) {
   882  	err := s.State.UpdateModelConfig(map[string]interface{}{key: block}, nil, nil)
   883  	c.Assert(err, jc.ErrorIsNil)
   884  }
   885  
   886  func (s *clientSuite) TestClientAddMachinesWithConstraints(c *gc.C) {
   887  	apiParams := make([]params.AddMachineParams, 3)
   888  	for i := 0; i < 3; i++ {
   889  		apiParams[i] = params.AddMachineParams{
   890  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   891  		}
   892  	}
   893  	// The last machine has some constraints.
   894  	apiParams[2].Constraints = constraints.MustParse("mem=4G")
   895  	machines, err := s.APIState.Client().AddMachines(apiParams)
   896  	c.Assert(err, jc.ErrorIsNil)
   897  	c.Assert(len(machines), gc.Equals, 3)
   898  	for i, machineResult := range machines {
   899  		c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
   900  		s.checkMachine(c, machineResult.Machine, series.LatestLts(), apiParams[i].Constraints.String())
   901  	}
   902  }
   903  
   904  func (s *clientSuite) TestClientAddMachinesWithPlacement(c *gc.C) {
   905  	apiParams := make([]params.AddMachineParams, 4)
   906  	for i := range apiParams {
   907  		apiParams[i] = params.AddMachineParams{
   908  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   909  		}
   910  	}
   911  	apiParams[0].Placement = instance.MustParsePlacement("lxd")
   912  	apiParams[1].Placement = instance.MustParsePlacement("lxd:0")
   913  	apiParams[1].ContainerType = instance.LXD
   914  	apiParams[2].Placement = instance.MustParsePlacement("controller:invalid")
   915  	apiParams[3].Placement = instance.MustParsePlacement("controller:valid")
   916  	machines, err := s.APIState.Client().AddMachines(apiParams)
   917  	c.Assert(err, jc.ErrorIsNil)
   918  	c.Assert(len(machines), gc.Equals, 4)
   919  	c.Assert(machines[0].Machine, gc.Equals, "0/lxd/0")
   920  	c.Assert(machines[1].Error, gc.ErrorMatches, "container type and placement are mutually exclusive")
   921  	c.Assert(machines[2].Error, gc.ErrorMatches, "cannot add a new machine: invalid placement is invalid")
   922  	c.Assert(machines[3].Machine, gc.Equals, "1")
   923  
   924  	m, err := s.BackingState.Machine(machines[3].Machine)
   925  	c.Assert(err, jc.ErrorIsNil)
   926  	c.Assert(m.Placement(), gc.DeepEquals, apiParams[3].Placement.Directive)
   927  }
   928  
   929  func (s *clientSuite) TestClientAddMachinesSomeErrors(c *gc.C) {
   930  	// Here we check that adding a number of containers correctly handles the
   931  	// case that some adds succeed and others fail and report the errors
   932  	// accordingly.
   933  	// We will set up params to the AddMachines API to attempt to create 3 machines.
   934  	// Machines 0 and 1 will be added successfully.
   935  	// Remaining machines will fail due to different reasons.
   936  
   937  	// Create a machine to host the requested containers.
   938  	host, err := s.State.AddMachine("quantal", state.JobHostUnits)
   939  	c.Assert(err, jc.ErrorIsNil)
   940  	// The host only supports lxd containers.
   941  	err = host.SetSupportedContainers([]instance.ContainerType{instance.LXD})
   942  	c.Assert(err, jc.ErrorIsNil)
   943  
   944  	// Set up params for adding 3 containers.
   945  	apiParams := make([]params.AddMachineParams, 3)
   946  	for i := range apiParams {
   947  		apiParams[i] = params.AddMachineParams{
   948  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   949  		}
   950  	}
   951  	// This will cause a add-machine to fail due to an unsupported container.
   952  	apiParams[2].ContainerType = instance.KVM
   953  	apiParams[2].ParentId = host.Id()
   954  	machines, err := s.APIState.Client().AddMachines(apiParams)
   955  	c.Assert(err, jc.ErrorIsNil)
   956  	c.Assert(len(machines), gc.Equals, 3)
   957  
   958  	// Check the results - machines 2 and 3 will have errors.
   959  	c.Check(machines[0].Machine, gc.Equals, "1")
   960  	c.Check(machines[0].Error, gc.IsNil)
   961  	c.Check(machines[1].Machine, gc.Equals, "2")
   962  	c.Check(machines[1].Error, gc.IsNil)
   963  	c.Check(machines[2].Error, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host kvm containers")
   964  }
   965  
   966  func (s *clientSuite) TestClientAddMachinesWithInstanceIdSomeErrors(c *gc.C) {
   967  	apiParams := make([]params.AddMachineParams, 3)
   968  	addrs := network.NewAddresses("1.2.3.4")
   969  	hc := instance.MustParseHardware("mem=4G")
   970  	for i := 0; i < 3; i++ {
   971  		apiParams[i] = params.AddMachineParams{
   972  			Jobs:       []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   973  			InstanceId: instance.Id(fmt.Sprintf("1234-%d", i)),
   974  			Nonce:      "foo",
   975  			HardwareCharacteristics: hc,
   976  			Addrs: params.FromNetworkAddresses(addrs...),
   977  		}
   978  	}
   979  	// This will cause the last add-machine to fail.
   980  	apiParams[2].Nonce = ""
   981  	machines, err := s.APIState.Client().AddMachines(apiParams)
   982  	c.Assert(err, jc.ErrorIsNil)
   983  	c.Assert(len(machines), gc.Equals, 3)
   984  	for i, machineResult := range machines {
   985  		if i == 2 {
   986  			c.Assert(machineResult.Error, gc.NotNil)
   987  			c.Assert(machineResult.Error, gc.ErrorMatches, "cannot add a new machine: cannot add a machine with an instance id and no nonce")
   988  		} else {
   989  			c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
   990  			s.checkMachine(c, machineResult.Machine, series.LatestLts(), apiParams[i].Constraints.String())
   991  			instanceId := fmt.Sprintf("1234-%d", i)
   992  			s.checkInstance(c, machineResult.Machine, instanceId, "foo", hc, addrs)
   993  		}
   994  	}
   995  }
   996  
   997  func (s *clientSuite) checkInstance(c *gc.C, id, instanceId, nonce string,
   998  	hc instance.HardwareCharacteristics, addr []network.Address) {
   999  
  1000  	machine, err := s.BackingState.Machine(id)
  1001  	c.Assert(err, jc.ErrorIsNil)
  1002  	machineInstanceId, err := machine.InstanceId()
  1003  	c.Assert(err, jc.ErrorIsNil)
  1004  	c.Assert(machine.CheckProvisioned(nonce), jc.IsTrue)
  1005  	c.Assert(machineInstanceId, gc.Equals, instance.Id(instanceId))
  1006  	machineHardware, err := machine.HardwareCharacteristics()
  1007  	c.Assert(err, jc.ErrorIsNil)
  1008  	c.Assert(machineHardware.String(), gc.Equals, hc.String())
  1009  	c.Assert(machine.Addresses(), gc.DeepEquals, addr)
  1010  }
  1011  
  1012  func (s *clientSuite) TestInjectMachinesStillExists(c *gc.C) {
  1013  	results := new(params.AddMachinesResults)
  1014  	// We need to use Call directly because the client interface
  1015  	// no longer refers to InjectMachine.
  1016  	args := params.AddMachines{
  1017  		MachineParams: []params.AddMachineParams{{
  1018  			Jobs:       []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  1019  			InstanceId: "i-foo",
  1020  			Nonce:      "nonce",
  1021  		}},
  1022  	}
  1023  	err := s.APIState.APICall("Client", 1, "", "AddMachines", args, &results)
  1024  	c.Assert(err, jc.ErrorIsNil)
  1025  	c.Assert(results.Machines, gc.HasLen, 1)
  1026  }
  1027  
  1028  func (s *clientSuite) TestProvisioningScript(c *gc.C) {
  1029  	// Inject a machine and then call the ProvisioningScript API.
  1030  	// The result should be the same as when calling MachineConfig,
  1031  	// converting it to a cloudinit.MachineConfig, and disabling
  1032  	// apt_upgrade.
  1033  	apiParams := params.AddMachineParams{
  1034  		Jobs:       []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  1035  		InstanceId: instance.Id("1234"),
  1036  		Nonce:      "foo",
  1037  		HardwareCharacteristics: instance.MustParseHardware("arch=amd64"),
  1038  	}
  1039  	machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams})
  1040  	c.Assert(err, jc.ErrorIsNil)
  1041  	c.Assert(len(machines), gc.Equals, 1)
  1042  	machineId := machines[0].Machine
  1043  	// Call ProvisioningScript. Normally ProvisioningScript and
  1044  	// MachineConfig are mutually exclusive; both of them will
  1045  	// allocate a api password for the machine agent.
  1046  	script, err := s.APIState.Client().ProvisioningScript(params.ProvisioningScriptParams{
  1047  		MachineId: machineId,
  1048  		Nonce:     apiParams.Nonce,
  1049  	})
  1050  	c.Assert(err, jc.ErrorIsNil)
  1051  	icfg, err := client.InstanceConfig(s.State, machineId, apiParams.Nonce, "")
  1052  	c.Assert(err, jc.ErrorIsNil)
  1053  	provisioningScript, err := manual.ProvisioningScript(icfg)
  1054  	c.Assert(err, jc.ErrorIsNil)
  1055  	// ProvisioningScript internally calls MachineConfig,
  1056  	// which allocates a new, random password. Everything
  1057  	// about the scripts should be the same other than
  1058  	// the line containing "oldpassword" from agent.conf.
  1059  	scriptLines := strings.Split(script, "\n")
  1060  	provisioningScriptLines := strings.Split(provisioningScript, "\n")
  1061  	c.Assert(scriptLines, gc.HasLen, len(provisioningScriptLines))
  1062  	for i, line := range scriptLines {
  1063  		if strings.Contains(line, "oldpassword") {
  1064  			continue
  1065  		}
  1066  		c.Assert(line, gc.Equals, provisioningScriptLines[i])
  1067  	}
  1068  }
  1069  
  1070  func (s *clientSuite) TestProvisioningScriptDisablePackageCommands(c *gc.C) {
  1071  	apiParams := params.AddMachineParams{
  1072  		Jobs:       []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  1073  		InstanceId: instance.Id("1234"),
  1074  		Nonce:      "foo",
  1075  		HardwareCharacteristics: instance.MustParseHardware("arch=amd64"),
  1076  	}
  1077  	machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams})
  1078  	c.Assert(err, jc.ErrorIsNil)
  1079  	c.Assert(len(machines), gc.Equals, 1)
  1080  	machineId := machines[0].Machine
  1081  
  1082  	provParams := params.ProvisioningScriptParams{
  1083  		MachineId: machineId,
  1084  		Nonce:     apiParams.Nonce,
  1085  	}
  1086  
  1087  	setUpdateBehavior := func(update, upgrade bool) {
  1088  		s.State.UpdateModelConfig(
  1089  			map[string]interface{}{
  1090  				"enable-os-upgrade":        upgrade,
  1091  				"enable-os-refresh-update": update,
  1092  			},
  1093  			nil,
  1094  			nil,
  1095  		)
  1096  	}
  1097  
  1098  	// Test enabling package commands
  1099  	provParams.DisablePackageCommands = false
  1100  	setUpdateBehavior(true, true)
  1101  	script, err := s.APIState.Client().ProvisioningScript(provParams)
  1102  	c.Assert(err, jc.ErrorIsNil)
  1103  	c.Check(script, jc.Contains, "apt-get update")
  1104  	c.Check(script, jc.Contains, "apt-get upgrade")
  1105  
  1106  	// Test disabling package commands
  1107  	provParams.DisablePackageCommands = true
  1108  	setUpdateBehavior(false, false)
  1109  	script, err = s.APIState.Client().ProvisioningScript(provParams)
  1110  	c.Assert(err, jc.ErrorIsNil)
  1111  	c.Check(script, gc.Not(jc.Contains), "apt-get update")
  1112  	c.Check(script, gc.Not(jc.Contains), "apt-get upgrade")
  1113  
  1114  	// Test client-specified DisablePackageCommands trumps environment
  1115  	// config variables.
  1116  	provParams.DisablePackageCommands = true
  1117  	setUpdateBehavior(true, true)
  1118  	script, err = s.APIState.Client().ProvisioningScript(provParams)
  1119  	c.Assert(err, jc.ErrorIsNil)
  1120  	c.Check(script, gc.Not(jc.Contains), "apt-get update")
  1121  	c.Check(script, gc.Not(jc.Contains), "apt-get upgrade")
  1122  
  1123  	// Test that in the abasence of a client-specified
  1124  	// DisablePackageCommands we use what's set in environment config.
  1125  	provParams.DisablePackageCommands = false
  1126  	setUpdateBehavior(false, false)
  1127  	//provParams.UpdateBehavior = &params.UpdateBehavior{false, false}
  1128  	script, err = s.APIState.Client().ProvisioningScript(provParams)
  1129  	c.Assert(err, jc.ErrorIsNil)
  1130  	c.Check(script, gc.Not(jc.Contains), "apt-get update")
  1131  	c.Check(script, gc.Not(jc.Contains), "apt-get upgrade")
  1132  }
  1133  
  1134  func (s *clientRepoSuite) TestResolveCharm(c *gc.C) {
  1135  	resolveCharmTests := []struct {
  1136  		about      string
  1137  		url        string
  1138  		resolved   string
  1139  		parseErr   string
  1140  		resolveErr string
  1141  	}{{
  1142  		about:    "wordpress resolved",
  1143  		url:      "cs:wordpress",
  1144  		resolved: "cs:trusty/wordpress",
  1145  	}, {
  1146  		about:    "mysql resolved",
  1147  		url:      "cs:mysql",
  1148  		resolved: "cs:precise/mysql",
  1149  	}, {
  1150  		about:    "riak resolved",
  1151  		url:      "cs:riak",
  1152  		resolved: "cs:trusty/riak",
  1153  	}, {
  1154  		about:    "fully qualified char reference",
  1155  		url:      "cs:utopic/riak-5",
  1156  		resolved: "cs:utopic/riak-5",
  1157  	}, {
  1158  		about:    "charm with series and no revision",
  1159  		url:      "cs:precise/wordpress",
  1160  		resolved: "cs:precise/wordpress",
  1161  	}, {
  1162  		about:      "fully qualified reference not found",
  1163  		url:        "cs:utopic/riak-42",
  1164  		resolveErr: `cannot resolve URL "cs:utopic/riak-42": charm not found`,
  1165  	}, {
  1166  		about:      "reference not found",
  1167  		url:        "cs:no-such",
  1168  		resolveErr: `cannot resolve URL "cs:no-such": charm or bundle not found`,
  1169  	}, {
  1170  		about:    "invalid charm name",
  1171  		url:      "cs:",
  1172  		parseErr: `cannot parse URL "cs://": name "" not valid`,
  1173  	}, {
  1174  		about:      "local charm",
  1175  		url:        "local:wordpress",
  1176  		resolveErr: `only charm store charm references are supported, with cs: schema`,
  1177  	}}
  1178  
  1179  	// Add some charms to be resolved later.
  1180  	for _, url := range []string{
  1181  		"precise/wordpress-1",
  1182  		"trusty/wordpress-2",
  1183  		"precise/mysql-3",
  1184  		"trusty/riak-4",
  1185  		"utopic/riak-5",
  1186  	} {
  1187  		s.UploadCharm(c, url, "wordpress")
  1188  	}
  1189  
  1190  	// Run the tests.
  1191  	for i, test := range resolveCharmTests {
  1192  		c.Logf("test %d: %s", i, test.about)
  1193  
  1194  		client := s.APIState.Client()
  1195  		ref, err := charm.ParseURL(test.url)
  1196  		if test.parseErr == "" {
  1197  			if c.Check(err, jc.ErrorIsNil) == false {
  1198  				continue
  1199  			}
  1200  		} else {
  1201  			if c.Check(err, gc.NotNil) == false {
  1202  				continue
  1203  			}
  1204  			c.Check(err, gc.ErrorMatches, test.parseErr)
  1205  			continue
  1206  		}
  1207  
  1208  		curl, err := client.ResolveCharm(ref)
  1209  		if test.resolveErr == "" {
  1210  			if c.Check(err, jc.ErrorIsNil) == false {
  1211  				continue
  1212  			}
  1213  			c.Check(curl.String(), gc.Equals, test.resolved)
  1214  			continue
  1215  		}
  1216  		c.Check(err, gc.ErrorMatches, test.resolveErr)
  1217  		c.Check(curl, gc.IsNil)
  1218  	}
  1219  }
  1220  
  1221  func (s *clientSuite) TestRetryProvisioning(c *gc.C) {
  1222  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1223  	c.Assert(err, jc.ErrorIsNil)
  1224  	now := time.Now()
  1225  	sInfo := status.StatusInfo{
  1226  		Status:  status.Error,
  1227  		Message: "error",
  1228  		Since:   &now,
  1229  	}
  1230  	err = machine.SetStatus(sInfo)
  1231  	c.Assert(err, jc.ErrorIsNil)
  1232  	_, err = s.APIState.Client().RetryProvisioning(machine.Tag().(names.MachineTag))
  1233  	c.Assert(err, jc.ErrorIsNil)
  1234  
  1235  	statusInfo, err := machine.Status()
  1236  	c.Assert(err, jc.ErrorIsNil)
  1237  	c.Assert(statusInfo.Status, gc.Equals, status.Error)
  1238  	c.Assert(statusInfo.Message, gc.Equals, "error")
  1239  	c.Assert(statusInfo.Data["transient"], jc.IsTrue)
  1240  }
  1241  
  1242  func (s *clientSuite) setupRetryProvisioning(c *gc.C) *state.Machine {
  1243  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1244  	c.Assert(err, jc.ErrorIsNil)
  1245  	now := time.Now()
  1246  	sInfo := status.StatusInfo{
  1247  		Status:  status.Error,
  1248  		Message: "error",
  1249  		Since:   &now,
  1250  	}
  1251  	err = machine.SetStatus(sInfo)
  1252  	c.Assert(err, jc.ErrorIsNil)
  1253  	return machine
  1254  }
  1255  
  1256  func (s *clientSuite) assertRetryProvisioning(c *gc.C, machine *state.Machine) {
  1257  	_, err := s.APIState.Client().RetryProvisioning(machine.Tag().(names.MachineTag))
  1258  	c.Assert(err, jc.ErrorIsNil)
  1259  	statusInfo, err := machine.Status()
  1260  	c.Assert(err, jc.ErrorIsNil)
  1261  	c.Assert(statusInfo.Status, gc.Equals, status.Error)
  1262  	c.Assert(statusInfo.Message, gc.Equals, "error")
  1263  	c.Assert(statusInfo.Data["transient"], jc.IsTrue)
  1264  }
  1265  
  1266  func (s *clientSuite) assertRetryProvisioningBlocked(c *gc.C, machine *state.Machine, msg string) {
  1267  	_, err := s.APIState.Client().RetryProvisioning(machine.Tag().(names.MachineTag))
  1268  	s.AssertBlocked(c, err, msg)
  1269  }
  1270  
  1271  func (s *clientSuite) TestBlockDestroyRetryProvisioning(c *gc.C) {
  1272  	m := s.setupRetryProvisioning(c)
  1273  	s.BlockDestroyModel(c, "TestBlockDestroyRetryProvisioning")
  1274  	s.assertRetryProvisioning(c, m)
  1275  }
  1276  
  1277  func (s *clientSuite) TestBlockRemoveRetryProvisioning(c *gc.C) {
  1278  	m := s.setupRetryProvisioning(c)
  1279  	s.BlockRemoveObject(c, "TestBlockRemoveRetryProvisioning")
  1280  	s.assertRetryProvisioning(c, m)
  1281  }
  1282  
  1283  func (s *clientSuite) TestBlockChangesRetryProvisioning(c *gc.C) {
  1284  	m := s.setupRetryProvisioning(c)
  1285  	s.BlockAllChanges(c, "TestBlockChangesRetryProvisioning")
  1286  	s.assertRetryProvisioningBlocked(c, m, "TestBlockChangesRetryProvisioning")
  1287  }
  1288  
  1289  func (s *clientSuite) TestAPIHostPorts(c *gc.C) {
  1290  	server1Addresses := []network.Address{{
  1291  		Value: "server-1",
  1292  		Type:  network.HostName,
  1293  		Scope: network.ScopePublic,
  1294  	}, {
  1295  		Value: "10.0.0.1",
  1296  		Type:  network.IPv4Address,
  1297  		Scope: network.ScopeCloudLocal,
  1298  	}}
  1299  	server2Addresses := []network.Address{{
  1300  		Value: "::1",
  1301  		Type:  network.IPv6Address,
  1302  		Scope: network.ScopeMachineLocal,
  1303  	}}
  1304  	stateAPIHostPorts := [][]network.HostPort{
  1305  		network.AddressesWithPort(server1Addresses, 123),
  1306  		network.AddressesWithPort(server2Addresses, 456),
  1307  	}
  1308  
  1309  	err := s.State.SetAPIHostPorts(stateAPIHostPorts)
  1310  	c.Assert(err, jc.ErrorIsNil)
  1311  	apiHostPorts, err := s.APIState.Client().APIHostPorts()
  1312  	c.Assert(err, jc.ErrorIsNil)
  1313  	c.Assert(apiHostPorts, gc.DeepEquals, stateAPIHostPorts)
  1314  }
  1315  
  1316  func (s *clientSuite) TestClientAgentVersion(c *gc.C) {
  1317  	current := version.MustParse("1.2.0")
  1318  	s.PatchValue(&jujuversion.Current, current)
  1319  	result, err := s.APIState.Client().AgentVersion()
  1320  	c.Assert(err, jc.ErrorIsNil)
  1321  	c.Assert(result, gc.Equals, current)
  1322  }
  1323  
  1324  func (s *clientSuite) assertDestroyMachineSuccess(c *gc.C, u *state.Unit, m0, m1, m2 *state.Machine) {
  1325  	err := s.APIState.Client().DestroyMachines("0", "1", "2")
  1326  	c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the model; machine 1 has unit "wordpress/0" assigned`)
  1327  	assertLife(c, m0, state.Alive)
  1328  	assertLife(c, m1, state.Alive)
  1329  	assertLife(c, m2, state.Dying)
  1330  
  1331  	err = u.UnassignFromMachine()
  1332  	c.Assert(err, jc.ErrorIsNil)
  1333  	err = s.APIState.Client().DestroyMachines("0", "1", "2")
  1334  	c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the model`)
  1335  	assertLife(c, m0, state.Alive)
  1336  	assertLife(c, m1, state.Dying)
  1337  	assertLife(c, m2, state.Dying)
  1338  }
  1339  
  1340  func (s *clientSuite) assertBlockedErrorAndLiveliness(
  1341  	c *gc.C,
  1342  	err error,
  1343  	msg string,
  1344  	living1 state.Living,
  1345  	living2 state.Living,
  1346  	living3 state.Living,
  1347  	living4 state.Living,
  1348  ) {
  1349  	s.AssertBlocked(c, err, msg)
  1350  	assertLife(c, living1, state.Alive)
  1351  	assertLife(c, living2, state.Alive)
  1352  	assertLife(c, living3, state.Alive)
  1353  	assertLife(c, living4, state.Alive)
  1354  }
  1355  
  1356  func (s *clientSuite) AssertBlocked(c *gc.C, err error, msg string) {
  1357  	c.Assert(params.IsCodeOperationBlocked(err), jc.IsTrue, gc.Commentf("error: %#v", err))
  1358  	c.Assert(errors.Cause(err), gc.DeepEquals, &rpc.RequestError{
  1359  		Message: msg,
  1360  		Code:    "operation is blocked",
  1361  	})
  1362  }
  1363  
  1364  func (s *clientSuite) TestBlockRemoveDestroyMachines(c *gc.C) {
  1365  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
  1366  	s.BlockRemoveObject(c, "TestBlockRemoveDestroyMachines")
  1367  	err := s.APIState.Client().DestroyMachines("0", "1", "2")
  1368  	s.assertBlockedErrorAndLiveliness(c, err, "TestBlockRemoveDestroyMachines", m0, m1, m2, u)
  1369  }
  1370  
  1371  func (s *clientSuite) TestBlockChangesDestroyMachines(c *gc.C) {
  1372  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
  1373  	s.BlockAllChanges(c, "TestBlockChangesDestroyMachines")
  1374  	err := s.APIState.Client().DestroyMachines("0", "1", "2")
  1375  	s.assertBlockedErrorAndLiveliness(c, err, "TestBlockChangesDestroyMachines", m0, m1, m2, u)
  1376  }
  1377  
  1378  func (s *clientSuite) TestBlockDestoryDestroyMachines(c *gc.C) {
  1379  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
  1380  	s.BlockDestroyModel(c, "TestBlockDestoryDestroyMachines")
  1381  	s.assertDestroyMachineSuccess(c, u, m0, m1, m2)
  1382  }
  1383  
  1384  func (s *clientSuite) TestAnyBlockForceDestroyMachines(c *gc.C) {
  1385  	// force bypasses all blocks
  1386  	s.BlockAllChanges(c, "TestAnyBlockForceDestroyMachines")
  1387  	s.BlockDestroyModel(c, "TestAnyBlockForceDestroyMachines")
  1388  	s.BlockRemoveObject(c, "TestAnyBlockForceDestroyMachines")
  1389  	s.assertForceDestroyMachines(c)
  1390  }
  1391  
  1392  func (s *clientSuite) assertForceDestroyMachines(c *gc.C) {
  1393  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
  1394  
  1395  	err := s.APIState.Client().ForceDestroyMachines("0", "1", "2")
  1396  	c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine is required by the model`)
  1397  	assertLife(c, m0, state.Alive)
  1398  	assertLife(c, m1, state.Alive)
  1399  	assertLife(c, m2, state.Alive)
  1400  	assertLife(c, u, state.Alive)
  1401  
  1402  	err = s.State.Cleanup()
  1403  	c.Assert(err, jc.ErrorIsNil)
  1404  	assertLife(c, m0, state.Alive)
  1405  	assertLife(c, m1, state.Dead)
  1406  	assertLife(c, m2, state.Dead)
  1407  	assertRemoved(c, u)
  1408  }