github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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  	"regexp"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/names"
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  	"gopkg.in/juju/charm.v5"
    18  	"gopkg.in/juju/charm.v5/charmrepo"
    19  
    20  	"github.com/juju/juju/agent"
    21  	"github.com/juju/juju/api"
    22  	"github.com/juju/juju/apiserver/client"
    23  	"github.com/juju/juju/apiserver/common"
    24  	"github.com/juju/juju/apiserver/params"
    25  	"github.com/juju/juju/apiserver/service"
    26  	"github.com/juju/juju/apiserver/testing"
    27  	apiservertesting "github.com/juju/juju/apiserver/testing"
    28  	"github.com/juju/juju/constraints"
    29  	"github.com/juju/juju/environs/config"
    30  	"github.com/juju/juju/environs/manual"
    31  	toolstesting "github.com/juju/juju/environs/tools/testing"
    32  	"github.com/juju/juju/instance"
    33  	"github.com/juju/juju/network"
    34  	"github.com/juju/juju/provider/dummy"
    35  	"github.com/juju/juju/state"
    36  	"github.com/juju/juju/state/multiwatcher"
    37  	"github.com/juju/juju/state/presence"
    38  	coretesting "github.com/juju/juju/testing"
    39  	"github.com/juju/juju/testing/factory"
    40  	"github.com/juju/juju/version"
    41  )
    42  
    43  type Killer interface {
    44  	Kill() error
    45  }
    46  
    47  type serverSuite struct {
    48  	baseSuite
    49  	client *client.Client
    50  }
    51  
    52  var _ = gc.Suite(&serverSuite{})
    53  
    54  func (s *serverSuite) SetUpTest(c *gc.C) {
    55  	s.baseSuite.SetUpTest(c)
    56  
    57  	var err error
    58  	auth := testing.FakeAuthorizer{
    59  		Tag:            s.AdminUserTag(c),
    60  		EnvironManager: true,
    61  	}
    62  	s.client, err = client.NewClient(s.State, common.NewResources(), auth)
    63  	c.Assert(err, jc.ErrorIsNil)
    64  }
    65  
    66  func (s *serverSuite) setAgentPresence(c *gc.C, machineId string) *presence.Pinger {
    67  	m, err := s.State.Machine(machineId)
    68  	c.Assert(err, jc.ErrorIsNil)
    69  	pinger, err := m.SetAgentPresence()
    70  	c.Assert(err, jc.ErrorIsNil)
    71  	s.State.StartSync()
    72  	err = m.WaitAgentPresence(coretesting.LongWait)
    73  	c.Assert(err, jc.ErrorIsNil)
    74  	return pinger
    75  }
    76  
    77  func (s *serverSuite) TestEnsureAvailabilityDeprecated(c *gc.C) {
    78  	_, err := s.State.AddMachine("quantal", state.JobManageEnviron)
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	// We have to ensure the agents are alive, or EnsureAvailability will
    81  	// create more to replace them.
    82  	pingerA := s.setAgentPresence(c, "0")
    83  	defer assertKill(c, pingerA)
    84  
    85  	machines, err := s.State.AllMachines()
    86  	c.Assert(err, jc.ErrorIsNil)
    87  	c.Assert(machines, gc.HasLen, 1)
    88  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
    89  
    90  	arg := params.StateServersSpecs{[]params.StateServersSpec{{NumStateServers: 3}}}
    91  	results, err := s.client.EnsureAvailability(arg)
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	c.Assert(results.Results, gc.HasLen, 1)
    94  	result := results.Results[0]
    95  	c.Assert(result.Error, gc.IsNil)
    96  	ensureAvailabilityResult := result.Result
    97  	c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"})
    98  	c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"})
    99  	c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0)
   100  
   101  	machines, err = s.State.AllMachines()
   102  	c.Assert(err, jc.ErrorIsNil)
   103  	c.Assert(machines, gc.HasLen, 3)
   104  	c.Assert(machines[0].Series(), gc.Equals, "quantal")
   105  	c.Assert(machines[1].Series(), gc.Equals, "quantal")
   106  	c.Assert(machines[2].Series(), gc.Equals, "quantal")
   107  }
   108  
   109  func (s *serverSuite) TestBlockEnsureAvailabilityDeprecated(c *gc.C) {
   110  	_, err := s.State.AddMachine("quantal", state.JobManageEnviron)
   111  	c.Assert(err, jc.ErrorIsNil)
   112  
   113  	s.BlockAllChanges(c, "TestBlockEnsureAvailabilityDeprecated")
   114  
   115  	arg := params.StateServersSpecs{[]params.StateServersSpec{{NumStateServers: 3}}}
   116  	results, err := s.client.EnsureAvailability(arg)
   117  	s.AssertBlocked(c, err, "TestBlockEnsureAvailabilityDeprecated")
   118  	c.Assert(results.Results, gc.HasLen, 0)
   119  
   120  	machines, err := s.State.AllMachines()
   121  	c.Assert(err, jc.ErrorIsNil)
   122  	c.Assert(machines, gc.HasLen, 1)
   123  }
   124  
   125  func (s *serverSuite) TestEnvUsersInfo(c *gc.C) {
   126  	testAdmin := s.AdminUserTag(c)
   127  	owner, err := s.State.EnvironmentUser(testAdmin)
   128  	c.Assert(err, jc.ErrorIsNil)
   129  
   130  	localUser1 := s.makeLocalEnvUser(c, "ralphdoe", "Ralph Doe")
   131  	localUser2 := s.makeLocalEnvUser(c, "samsmith", "Sam Smith")
   132  	remoteUser1 := s.Factory.MakeEnvUser(c, &factory.EnvUserParams{User: "bobjohns@ubuntuone", DisplayName: "Bob Johns"})
   133  	remoteUser2 := s.Factory.MakeEnvUser(c, &factory.EnvUserParams{User: "nicshaw@idprovider", DisplayName: "Nic Shaw"})
   134  
   135  	results, err := s.client.EnvUserInfo()
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	expected := params.EnvUserInfoResults{
   138  		Results: []params.EnvUserInfoResult{
   139  			{
   140  				Result: &params.EnvUserInfo{
   141  					UserName:       owner.UserName(),
   142  					DisplayName:    owner.DisplayName(),
   143  					CreatedBy:      owner.UserName(),
   144  					DateCreated:    owner.DateCreated(),
   145  					LastConnection: owner.LastConnection(),
   146  				},
   147  			}, {
   148  				Result: &params.EnvUserInfo{
   149  					UserName:       "ralphdoe@local",
   150  					DisplayName:    "Ralph Doe",
   151  					CreatedBy:      owner.UserName(),
   152  					DateCreated:    localUser1.DateCreated(),
   153  					LastConnection: localUser1.LastConnection(),
   154  				},
   155  			}, {
   156  				Result: &params.EnvUserInfo{
   157  					UserName:       "samsmith@local",
   158  					DisplayName:    "Sam Smith",
   159  					CreatedBy:      owner.UserName(),
   160  					DateCreated:    localUser2.DateCreated(),
   161  					LastConnection: localUser2.LastConnection(),
   162  				},
   163  			}, {
   164  				Result: &params.EnvUserInfo{
   165  					UserName:       "bobjohns@ubuntuone",
   166  					DisplayName:    "Bob Johns",
   167  					CreatedBy:      owner.UserName(),
   168  					DateCreated:    remoteUser1.DateCreated(),
   169  					LastConnection: remoteUser1.LastConnection(),
   170  				},
   171  			}, {
   172  				Result: &params.EnvUserInfo{
   173  					UserName:       "nicshaw@idprovider",
   174  					DisplayName:    "Nic Shaw",
   175  					CreatedBy:      owner.UserName(),
   176  					DateCreated:    remoteUser2.DateCreated(),
   177  					LastConnection: remoteUser2.LastConnection(),
   178  				},
   179  			}},
   180  	}
   181  
   182  	sort.Sort(ByUserName(expected.Results))
   183  	sort.Sort(ByUserName(results.Results))
   184  	c.Assert(results, jc.DeepEquals, expected)
   185  }
   186  
   187  // ByUserName implements sort.Interface for []params.EnvUserInfoResult based on
   188  // the UserName field.
   189  type ByUserName []params.EnvUserInfoResult
   190  
   191  func (a ByUserName) Len() int           { return len(a) }
   192  func (a ByUserName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   193  func (a ByUserName) Less(i, j int) bool { return a[i].Result.UserName < a[j].Result.UserName }
   194  
   195  func (s *serverSuite) makeLocalEnvUser(c *gc.C, username, displayname string) *state.EnvironmentUser {
   196  	// factory.MakeUser will create an EnvUser for a local user by defalut
   197  	user := s.Factory.MakeUser(c, &factory.UserParams{Name: username, DisplayName: displayname})
   198  	envUser, err := s.State.EnvironmentUser(user.UserTag())
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	return envUser
   201  }
   202  
   203  func (s *serverSuite) TestShareEnvironmentAddMissingLocalFails(c *gc.C) {
   204  	args := params.ModifyEnvironUsers{
   205  		Changes: []params.ModifyEnvironUser{{
   206  			UserTag: names.NewLocalUserTag("foobar").String(),
   207  			Action:  params.AddEnvUser,
   208  		}}}
   209  
   210  	result, err := s.client.ShareEnvironment(args)
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	expectedErr := `could not share environment: user "foobar" does not exist locally: user "foobar" not found`
   213  	c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   214  	c.Assert(result.Results, gc.HasLen, 1)
   215  	c.Assert(result.Results[0].Error, gc.ErrorMatches, expectedErr)
   216  }
   217  
   218  func (s *serverSuite) TestUnshareEnvironment(c *gc.C) {
   219  	user := s.Factory.MakeEnvUser(c, nil)
   220  	_, err := s.State.EnvironmentUser(user.UserTag())
   221  	c.Assert(err, jc.ErrorIsNil)
   222  
   223  	args := params.ModifyEnvironUsers{
   224  		Changes: []params.ModifyEnvironUser{{
   225  			UserTag: user.UserTag().String(),
   226  			Action:  params.RemoveEnvUser,
   227  		}}}
   228  
   229  	result, err := s.client.ShareEnvironment(args)
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	c.Assert(result.OneError(), gc.IsNil)
   232  	c.Assert(result.Results, gc.HasLen, 1)
   233  	c.Assert(result.Results[0].Error, gc.IsNil)
   234  
   235  	_, err = s.State.EnvironmentUser(user.UserTag())
   236  	c.Assert(errors.IsNotFound(err), jc.IsTrue)
   237  }
   238  
   239  func (s *serverSuite) TestUnshareEnvironmentMissingUser(c *gc.C) {
   240  	user := names.NewUserTag("bob")
   241  	args := params.ModifyEnvironUsers{
   242  		Changes: []params.ModifyEnvironUser{{
   243  			UserTag: user.String(),
   244  			Action:  params.RemoveEnvUser,
   245  		}}}
   246  
   247  	result, err := s.client.ShareEnvironment(args)
   248  	c.Assert(err, jc.ErrorIsNil)
   249  	c.Assert(result.OneError(), gc.ErrorMatches, `could not unshare environment: env user "bob@local" does not exist: transaction aborted`)
   250  
   251  	c.Assert(result.Results, gc.HasLen, 1)
   252  	c.Assert(result.Results[0].Error, gc.NotNil)
   253  
   254  	_, err = s.State.EnvironmentUser(user)
   255  	c.Assert(errors.IsNotFound(err), jc.IsTrue)
   256  }
   257  
   258  func (s *serverSuite) TestShareEnvironmentAddLocalUser(c *gc.C) {
   259  	user := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar", NoEnvUser: true})
   260  	args := params.ModifyEnvironUsers{
   261  		Changes: []params.ModifyEnvironUser{{
   262  			UserTag: user.Tag().String(),
   263  			Action:  params.AddEnvUser,
   264  		}}}
   265  
   266  	result, err := s.client.ShareEnvironment(args)
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	c.Assert(result.OneError(), gc.IsNil)
   269  	c.Assert(result.Results, gc.HasLen, 1)
   270  	c.Assert(result.Results[0].Error, gc.IsNil)
   271  
   272  	envUser, err := s.State.EnvironmentUser(user.UserTag())
   273  	c.Assert(err, jc.ErrorIsNil)
   274  	c.Assert(envUser.UserName(), gc.Equals, user.UserTag().Username())
   275  	c.Assert(envUser.CreatedBy(), gc.Equals, dummy.AdminUserTag().Username())
   276  	c.Assert(envUser.LastConnection(), gc.IsNil)
   277  }
   278  
   279  func (s *serverSuite) TestShareEnvironmentAddRemoteUser(c *gc.C) {
   280  	user := names.NewUserTag("foobar@ubuntuone")
   281  	args := params.ModifyEnvironUsers{
   282  		Changes: []params.ModifyEnvironUser{{
   283  			UserTag: user.String(),
   284  			Action:  params.AddEnvUser,
   285  		}}}
   286  
   287  	result, err := s.client.ShareEnvironment(args)
   288  	c.Assert(err, jc.ErrorIsNil)
   289  	c.Assert(result.OneError(), gc.IsNil)
   290  	c.Assert(result.Results, gc.HasLen, 1)
   291  	c.Assert(result.Results[0].Error, gc.IsNil)
   292  
   293  	envUser, err := s.State.EnvironmentUser(user)
   294  	c.Assert(err, jc.ErrorIsNil)
   295  	c.Assert(envUser.UserName(), gc.Equals, user.Username())
   296  	c.Assert(envUser.CreatedBy(), gc.Equals, dummy.AdminUserTag().Username())
   297  	c.Assert(envUser.LastConnection(), gc.IsNil)
   298  }
   299  
   300  func (s *serverSuite) TestShareEnvironmentAddUserTwice(c *gc.C) {
   301  	user := s.Factory.MakeUser(c, &factory.UserParams{Name: "foobar"})
   302  	args := params.ModifyEnvironUsers{
   303  		Changes: []params.ModifyEnvironUser{{
   304  			UserTag: user.Tag().String(),
   305  			Action:  params.AddEnvUser,
   306  		}}}
   307  
   308  	_, err := s.client.ShareEnvironment(args)
   309  	c.Assert(err, jc.ErrorIsNil)
   310  
   311  	result, err := s.client.ShareEnvironment(args)
   312  	c.Assert(err, jc.ErrorIsNil)
   313  	c.Assert(result.OneError(), gc.ErrorMatches, "could not share environment: environment user \"foobar@local\" already exists")
   314  	c.Assert(result.Results, gc.HasLen, 1)
   315  	c.Assert(result.Results[0].Error, gc.ErrorMatches, "could not share environment: environment user \"foobar@local\" already exists")
   316  	c.Assert(result.Results[0].Error.Code, gc.Matches, params.CodeAlreadyExists)
   317  
   318  	envUser, err := s.State.EnvironmentUser(user.UserTag())
   319  	c.Assert(err, jc.ErrorIsNil)
   320  	c.Assert(envUser.UserName(), gc.Equals, user.UserTag().Username())
   321  }
   322  
   323  func (s *serverSuite) TestShareEnvironmentInvalidTags(c *gc.C) {
   324  	for _, testParam := range []struct {
   325  		tag      string
   326  		validTag bool
   327  	}{{
   328  		tag:      "unit-foo/0",
   329  		validTag: true,
   330  	}, {
   331  		tag:      "service-foo",
   332  		validTag: true,
   333  	}, {
   334  		tag:      "relation-wordpress:db mysql:db",
   335  		validTag: true,
   336  	}, {
   337  		tag:      "machine-0",
   338  		validTag: true,
   339  	}, {
   340  		tag:      "user@local",
   341  		validTag: false,
   342  	}, {
   343  		tag:      "user-Mua^h^h^h^arh",
   344  		validTag: true,
   345  	}, {
   346  		tag:      "user@",
   347  		validTag: false,
   348  	}, {
   349  		tag:      "user@ubuntuone",
   350  		validTag: false,
   351  	}, {
   352  		tag:      "user@ubuntuone",
   353  		validTag: false,
   354  	}, {
   355  		tag:      "@ubuntuone",
   356  		validTag: false,
   357  	}, {
   358  		tag:      "in^valid.",
   359  		validTag: false,
   360  	}, {
   361  		tag:      "",
   362  		validTag: false,
   363  	},
   364  	} {
   365  		var expectedErr string
   366  		errPart := `could not share environment: "` + regexp.QuoteMeta(testParam.tag) + `" is not a valid `
   367  
   368  		if testParam.validTag {
   369  
   370  			// The string is a valid tag, but not a user tag.
   371  			expectedErr = errPart + `user tag`
   372  		} else {
   373  
   374  			// The string is not a valid tag of any kind.
   375  			expectedErr = errPart + `tag`
   376  		}
   377  
   378  		args := params.ModifyEnvironUsers{
   379  			Changes: []params.ModifyEnvironUser{{
   380  				UserTag: testParam.tag,
   381  				Action:  params.AddEnvUser,
   382  			}}}
   383  
   384  		_, err := s.client.ShareEnvironment(args)
   385  		result, err := s.client.ShareEnvironment(args)
   386  		c.Assert(err, jc.ErrorIsNil)
   387  		c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   388  		c.Assert(result.Results, gc.HasLen, 1)
   389  		c.Assert(result.Results[0].Error, gc.ErrorMatches, expectedErr)
   390  	}
   391  }
   392  
   393  func (s *serverSuite) TestShareEnvironmentZeroArgs(c *gc.C) {
   394  	args := params.ModifyEnvironUsers{Changes: []params.ModifyEnvironUser{{}}}
   395  
   396  	_, err := s.client.ShareEnvironment(args)
   397  	result, err := s.client.ShareEnvironment(args)
   398  	c.Assert(err, jc.ErrorIsNil)
   399  	expectedErr := `could not share environment: "" is not a valid tag`
   400  	c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   401  	c.Assert(result.Results, gc.HasLen, 1)
   402  	c.Assert(result.Results[0].Error, gc.ErrorMatches, expectedErr)
   403  }
   404  
   405  func (s *serverSuite) TestShareEnvironmentInvalidAction(c *gc.C) {
   406  	var dance params.EnvironAction = "dance"
   407  	args := params.ModifyEnvironUsers{
   408  		Changes: []params.ModifyEnvironUser{{
   409  			UserTag: "user-user@local",
   410  			Action:  dance,
   411  		}}}
   412  
   413  	_, err := s.client.ShareEnvironment(args)
   414  	result, err := s.client.ShareEnvironment(args)
   415  	c.Assert(err, jc.ErrorIsNil)
   416  	expectedErr := `unknown action "dance"`
   417  	c.Assert(result.OneError(), gc.ErrorMatches, expectedErr)
   418  	c.Assert(result.Results, gc.HasLen, 1)
   419  	c.Assert(result.Results[0].Error, gc.ErrorMatches, expectedErr)
   420  }
   421  
   422  func (s *serverSuite) TestSetEnvironAgentVersion(c *gc.C) {
   423  	args := params.SetEnvironAgentVersion{
   424  		Version: version.MustParse("9.8.7"),
   425  	}
   426  	err := s.client.SetEnvironAgentVersion(args)
   427  	c.Assert(err, jc.ErrorIsNil)
   428  
   429  	envConfig, err := s.State.EnvironConfig()
   430  	c.Assert(err, jc.ErrorIsNil)
   431  	agentVersion, found := envConfig.AllAttrs()["agent-version"]
   432  	c.Assert(found, jc.IsTrue)
   433  	c.Assert(agentVersion, gc.Equals, "9.8.7")
   434  }
   435  
   436  func (s *serverSuite) assertSetEnvironAgentVersion(c *gc.C) {
   437  	args := params.SetEnvironAgentVersion{
   438  		Version: version.MustParse("9.8.7"),
   439  	}
   440  	err := s.client.SetEnvironAgentVersion(args)
   441  	c.Assert(err, jc.ErrorIsNil)
   442  	envConfig, err := s.State.EnvironConfig()
   443  	c.Assert(err, jc.ErrorIsNil)
   444  	agentVersion, found := envConfig.AllAttrs()["agent-version"]
   445  	c.Assert(found, jc.IsTrue)
   446  	c.Assert(agentVersion, gc.Equals, "9.8.7")
   447  }
   448  
   449  func (s *serverSuite) assertSetEnvironAgentVersionBlocked(c *gc.C, msg string) {
   450  	args := params.SetEnvironAgentVersion{
   451  		Version: version.MustParse("9.8.7"),
   452  	}
   453  	err := s.client.SetEnvironAgentVersion(args)
   454  	s.AssertBlocked(c, err, msg)
   455  }
   456  
   457  func (s *serverSuite) TestBlockDestroySetEnvironAgentVersion(c *gc.C) {
   458  	s.BlockDestroyEnvironment(c, "TestBlockDestroySetEnvironAgentVersion")
   459  	s.assertSetEnvironAgentVersion(c)
   460  }
   461  
   462  func (s *serverSuite) TestBlockRemoveSetEnvironAgentVersion(c *gc.C) {
   463  	s.BlockRemoveObject(c, "TestBlockRemoveSetEnvironAgentVersion")
   464  	s.assertSetEnvironAgentVersion(c)
   465  }
   466  
   467  func (s *serverSuite) TestBlockChangesSetEnvironAgentVersion(c *gc.C) {
   468  	s.BlockAllChanges(c, "TestBlockChangesSetEnvironAgentVersion")
   469  	s.assertSetEnvironAgentVersionBlocked(c, "TestBlockChangesSetEnvironAgentVersion")
   470  }
   471  
   472  func (s *serverSuite) TestAbortCurrentUpgrade(c *gc.C) {
   473  	// Create a provisioned state server.
   474  	machine, err := s.State.AddMachine("series", state.JobManageEnviron)
   475  	c.Assert(err, jc.ErrorIsNil)
   476  	err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil)
   477  	c.Assert(err, jc.ErrorIsNil)
   478  
   479  	// Start an upgrade.
   480  	_, err = s.State.EnsureUpgradeInfo(
   481  		machine.Id(),
   482  		version.MustParse("1.2.3"),
   483  		version.MustParse("9.8.7"),
   484  	)
   485  	c.Assert(err, jc.ErrorIsNil)
   486  	isUpgrading, err := s.State.IsUpgrading()
   487  	c.Assert(err, jc.ErrorIsNil)
   488  	c.Assert(isUpgrading, jc.IsTrue)
   489  
   490  	// Abort it.
   491  	err = s.client.AbortCurrentUpgrade()
   492  	c.Assert(err, jc.ErrorIsNil)
   493  
   494  	isUpgrading, err = s.State.IsUpgrading()
   495  	c.Assert(err, jc.ErrorIsNil)
   496  	c.Assert(isUpgrading, jc.IsFalse)
   497  }
   498  
   499  func (s *serverSuite) assertAbortCurrentUpgradeBlocked(c *gc.C, msg string) {
   500  	err := s.client.AbortCurrentUpgrade()
   501  	s.AssertBlocked(c, err, msg)
   502  }
   503  
   504  func (s *serverSuite) assertAbortCurrentUpgrade(c *gc.C) {
   505  	err := s.client.AbortCurrentUpgrade()
   506  	c.Assert(err, jc.ErrorIsNil)
   507  	isUpgrading, err := s.State.IsUpgrading()
   508  	c.Assert(err, jc.ErrorIsNil)
   509  	c.Assert(isUpgrading, jc.IsFalse)
   510  }
   511  
   512  func (s *serverSuite) setupAbortCurrentUpgradeBlocked(c *gc.C) {
   513  	// Create a provisioned state server.
   514  	machine, err := s.State.AddMachine("series", state.JobManageEnviron)
   515  	c.Assert(err, jc.ErrorIsNil)
   516  	err = machine.SetProvisioned(instance.Id("i-blah"), "fake-nonce", nil)
   517  	c.Assert(err, jc.ErrorIsNil)
   518  
   519  	// Start an upgrade.
   520  	_, err = s.State.EnsureUpgradeInfo(
   521  		machine.Id(),
   522  		version.MustParse("1.2.3"),
   523  		version.MustParse("9.8.7"),
   524  	)
   525  	c.Assert(err, jc.ErrorIsNil)
   526  	isUpgrading, err := s.State.IsUpgrading()
   527  	c.Assert(err, jc.ErrorIsNil)
   528  	c.Assert(isUpgrading, jc.IsTrue)
   529  }
   530  
   531  func (s *serverSuite) TestBlockDestroyAbortCurrentUpgrade(c *gc.C) {
   532  	s.setupAbortCurrentUpgradeBlocked(c)
   533  	s.BlockDestroyEnvironment(c, "TestBlockDestroyAbortCurrentUpgrade")
   534  	s.assertAbortCurrentUpgrade(c)
   535  }
   536  
   537  func (s *serverSuite) TestBlockRemoveAbortCurrentUpgrade(c *gc.C) {
   538  	s.setupAbortCurrentUpgradeBlocked(c)
   539  	s.BlockRemoveObject(c, "TestBlockRemoveAbortCurrentUpgrade")
   540  	s.assertAbortCurrentUpgrade(c)
   541  }
   542  
   543  func (s *serverSuite) TestBlockChangesAbortCurrentUpgrade(c *gc.C) {
   544  	s.setupAbortCurrentUpgradeBlocked(c)
   545  	s.BlockAllChanges(c, "TestBlockChangesAbortCurrentUpgrade")
   546  	s.assertAbortCurrentUpgradeBlocked(c, "TestBlockChangesAbortCurrentUpgrade")
   547  }
   548  
   549  type clientSuite struct {
   550  	baseSuite
   551  }
   552  
   553  var _ = gc.Suite(&clientSuite{})
   554  
   555  // clearSinceTimes zeros out the updated timestamps inside status
   556  // so we can easily check the results.
   557  func clearSinceTimes(status *api.Status) {
   558  	for serviceId, service := range status.Services {
   559  		for unitId, unit := range service.Units {
   560  			unit.Workload.Since = nil
   561  			unit.UnitAgent.Since = nil
   562  			for id, subord := range unit.Subordinates {
   563  				subord.Workload.Since = nil
   564  				subord.UnitAgent.Since = nil
   565  				unit.Subordinates[id] = subord
   566  			}
   567  			service.Units[unitId] = unit
   568  		}
   569  		service.Status.Since = nil
   570  		status.Services[serviceId] = service
   571  	}
   572  	for id, machine := range status.Machines {
   573  		machine.Agent.Since = nil
   574  		status.Machines[id] = machine
   575  	}
   576  }
   577  
   578  func (s *clientSuite) TestClientStatus(c *gc.C) {
   579  	s.setUpScenario(c)
   580  	status, err := s.APIState.Client().Status(nil)
   581  	clearSinceTimes(status)
   582  	c.Assert(err, jc.ErrorIsNil)
   583  	c.Assert(status, jc.DeepEquals, scenarioStatus)
   584  }
   585  
   586  var (
   587  	validSetTestValue     = "a value with spaces\nand newline\nand UTF-8 characters: \U0001F604 / \U0001F44D"
   588  	invalidSetTestValue   = "a value with an invalid UTF-8 sequence: " + string([]byte{0xFF, 0xFF})
   589  	correctedSetTestValue = "a value with an invalid UTF-8 sequence: \ufffd\ufffd"
   590  )
   591  
   592  func (s *clientSuite) TestClientServiceSet(c *gc.C) {
   593  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   594  
   595  	err := s.APIState.Client().ServiceSet("dummy", map[string]string{
   596  		"title":    "foobar",
   597  		"username": validSetTestValue,
   598  	})
   599  	c.Assert(err, jc.ErrorIsNil)
   600  	settings, err := dummy.ConfigSettings()
   601  	c.Assert(err, jc.ErrorIsNil)
   602  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   603  		"title":    "foobar",
   604  		"username": validSetTestValue,
   605  	})
   606  
   607  	// Test doesn't fail because Go JSON marshalling converts invalid
   608  	// UTF-8 sequences transparently to U+FFFD. The test demonstrates
   609  	// this behavior. It's a currently accepted behavior as it never has
   610  	// been a real-life issue.
   611  	err = s.APIState.Client().ServiceSet("dummy", map[string]string{
   612  		"title":    "foobar",
   613  		"username": invalidSetTestValue,
   614  	})
   615  	c.Assert(err, jc.ErrorIsNil)
   616  	settings, err = dummy.ConfigSettings()
   617  	c.Assert(err, jc.ErrorIsNil)
   618  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   619  		"title":    "foobar",
   620  		"username": correctedSetTestValue,
   621  	})
   622  
   623  	err = s.APIState.Client().ServiceSet("dummy", map[string]string{
   624  		"title":    "barfoo",
   625  		"username": "",
   626  	})
   627  	c.Assert(err, jc.ErrorIsNil)
   628  	settings, err = dummy.ConfigSettings()
   629  	c.Assert(err, jc.ErrorIsNil)
   630  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   631  		"title":    "barfoo",
   632  		"username": "",
   633  	})
   634  }
   635  
   636  func (s *serverSuite) assertServiceSetBlocked(c *gc.C, dummy *state.Service, msg string) {
   637  	err := s.client.ServiceSet(params.ServiceSet{
   638  		ServiceName: "dummy",
   639  		Options: map[string]string{
   640  			"title":    "foobar",
   641  			"username": validSetTestValue}})
   642  	s.AssertBlocked(c, err, msg)
   643  }
   644  
   645  func (s *serverSuite) assertServiceSet(c *gc.C, dummy *state.Service) {
   646  	err := s.client.ServiceSet(params.ServiceSet{
   647  		ServiceName: "dummy",
   648  		Options: map[string]string{
   649  			"title":    "foobar",
   650  			"username": validSetTestValue}})
   651  	c.Assert(err, jc.ErrorIsNil)
   652  	settings, err := dummy.ConfigSettings()
   653  	c.Assert(err, jc.ErrorIsNil)
   654  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   655  		"title":    "foobar",
   656  		"username": validSetTestValue,
   657  	})
   658  }
   659  
   660  func (s *serverSuite) TestBlockDestroyServiceSet(c *gc.C) {
   661  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   662  	s.BlockDestroyEnvironment(c, "TestBlockDestroyServiceSet")
   663  	s.assertServiceSet(c, dummy)
   664  }
   665  
   666  func (s *serverSuite) TestBlockRemoveServiceSet(c *gc.C) {
   667  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   668  	s.BlockRemoveObject(c, "TestBlockRemoveServiceSet")
   669  	s.assertServiceSet(c, dummy)
   670  }
   671  
   672  func (s *serverSuite) TestBlockChangesServiceSet(c *gc.C) {
   673  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   674  	s.BlockAllChanges(c, "TestBlockChangesServiceSet")
   675  	s.assertServiceSetBlocked(c, dummy, "TestBlockChangesServiceSet")
   676  }
   677  
   678  func (s *clientSuite) TestClientServerUnset(c *gc.C) {
   679  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   680  
   681  	err := s.APIState.Client().ServiceSet("dummy", map[string]string{
   682  		"title":    "foobar",
   683  		"username": "user name",
   684  	})
   685  	c.Assert(err, jc.ErrorIsNil)
   686  	settings, err := dummy.ConfigSettings()
   687  	c.Assert(err, jc.ErrorIsNil)
   688  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   689  		"title":    "foobar",
   690  		"username": "user name",
   691  	})
   692  
   693  	err = s.APIState.Client().ServiceUnset("dummy", []string{"username"})
   694  	c.Assert(err, jc.ErrorIsNil)
   695  	settings, err = dummy.ConfigSettings()
   696  	c.Assert(err, jc.ErrorIsNil)
   697  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   698  		"title": "foobar",
   699  	})
   700  }
   701  
   702  func (s *serverSuite) setupServerUnsetBlocked(c *gc.C) *state.Service {
   703  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   704  
   705  	err := s.client.ServiceSet(params.ServiceSet{
   706  		ServiceName: "dummy",
   707  		Options: map[string]string{
   708  			"title":    "foobar",
   709  			"username": "user name",
   710  		}})
   711  	c.Assert(err, jc.ErrorIsNil)
   712  	settings, err := dummy.ConfigSettings()
   713  	c.Assert(err, jc.ErrorIsNil)
   714  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   715  		"title":    "foobar",
   716  		"username": "user name",
   717  	})
   718  	return dummy
   719  }
   720  
   721  func (s *serverSuite) assertServerUnset(c *gc.C, dummy *state.Service) {
   722  	err := s.client.ServiceUnset(params.ServiceUnset{
   723  		ServiceName: "dummy",
   724  		Options:     []string{"username"},
   725  	})
   726  	c.Assert(err, jc.ErrorIsNil)
   727  	settings, err := dummy.ConfigSettings()
   728  	c.Assert(err, jc.ErrorIsNil)
   729  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   730  		"title": "foobar",
   731  	})
   732  }
   733  
   734  func (s *serverSuite) assertServerUnsetBlocked(c *gc.C, dummy *state.Service, msg string) {
   735  	err := s.client.ServiceUnset(params.ServiceUnset{
   736  		ServiceName: "dummy",
   737  		Options:     []string{"username"},
   738  	})
   739  	s.AssertBlocked(c, err, msg)
   740  }
   741  
   742  func (s *serverSuite) TestBlockDestroyServerUnset(c *gc.C) {
   743  	dummy := s.setupServerUnsetBlocked(c)
   744  	s.BlockDestroyEnvironment(c, "TestBlockDestroyServerUnset")
   745  	s.assertServerUnset(c, dummy)
   746  }
   747  
   748  func (s *serverSuite) TestBlockRemoveServerUnset(c *gc.C) {
   749  	dummy := s.setupServerUnsetBlocked(c)
   750  	s.BlockRemoveObject(c, "TestBlockRemoveServerUnset")
   751  	s.assertServerUnset(c, dummy)
   752  }
   753  
   754  func (s *serverSuite) TestBlockChangesServerUnset(c *gc.C) {
   755  	dummy := s.setupServerUnsetBlocked(c)
   756  	s.BlockAllChanges(c, "TestBlockChangesServerUnset")
   757  	s.assertServerUnsetBlocked(c, dummy, "TestBlockChangesServerUnset")
   758  }
   759  
   760  func (s *clientSuite) TestClientServiceSetYAML(c *gc.C) {
   761  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   762  
   763  	err := s.APIState.Client().ServiceSetYAML("dummy", "dummy:\n  title: foobar\n  username: user name\n")
   764  	c.Assert(err, jc.ErrorIsNil)
   765  	settings, err := dummy.ConfigSettings()
   766  	c.Assert(err, jc.ErrorIsNil)
   767  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   768  		"title":    "foobar",
   769  		"username": "user name",
   770  	})
   771  
   772  	err = s.APIState.Client().ServiceSetYAML("dummy", "dummy:\n  title: barfoo\n  username: \n")
   773  	c.Assert(err, jc.ErrorIsNil)
   774  	settings, err = dummy.ConfigSettings()
   775  	c.Assert(err, jc.ErrorIsNil)
   776  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   777  		"title": "barfoo",
   778  	})
   779  }
   780  
   781  func (s *clientSuite) assertServiceSetYAML(c *gc.C, dummy *state.Service) {
   782  	err := s.APIState.Client().ServiceSetYAML("dummy", "dummy:\n  title: foobar\n  username: user name\n")
   783  	c.Assert(err, jc.ErrorIsNil)
   784  	settings, err := dummy.ConfigSettings()
   785  	c.Assert(err, jc.ErrorIsNil)
   786  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   787  		"title":    "foobar",
   788  		"username": "user name",
   789  	})
   790  }
   791  
   792  func (s *clientSuite) assertServiceSetYAMLBlocked(c *gc.C, dummy *state.Service, msg string) {
   793  	err := s.APIState.Client().ServiceSetYAML("dummy", "dummy:\n  title: foobar\n  username: user name\n")
   794  	s.AssertBlocked(c, err, msg)
   795  }
   796  
   797  func (s *clientSuite) TestBlockDestroyServiceSetYAML(c *gc.C) {
   798  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   799  	s.BlockDestroyEnvironment(c, "TestBlockDestroyServiceSetYAML")
   800  	s.assertServiceSetYAML(c, dummy)
   801  }
   802  
   803  func (s *clientSuite) TestBlockRemoveServiceSetYAML(c *gc.C) {
   804  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   805  	s.BlockRemoveObject(c, "TestBlockRemoveServiceSetYAML")
   806  	s.assertServiceSetYAML(c, dummy)
   807  }
   808  
   809  func (s *clientSuite) TestBlockChangesServiceSetYAML(c *gc.C) {
   810  	dummy := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   811  	s.BlockAllChanges(c, "TestBlockChangesServiceSetYAML")
   812  	s.assertServiceSetYAMLBlocked(c, dummy, "TestBlockChangesServiceSetYAML")
   813  }
   814  
   815  var clientAddServiceUnitsTests = []struct {
   816  	about    string
   817  	service  string // if not set, defaults to 'dummy'
   818  	expected []string
   819  	to       string
   820  	err      string
   821  }{
   822  	{
   823  		about:    "returns unit names",
   824  		expected: []string{"dummy/0", "dummy/1", "dummy/2"},
   825  	},
   826  	{
   827  		about: "fails trying to add zero units",
   828  		err:   "must add at least one unit",
   829  	},
   830  	{
   831  		about:    "cannot mix to when adding multiple units",
   832  		err:      "cannot use NumUnits with ToMachineSpec",
   833  		expected: []string{"dummy/0", "dummy/1"},
   834  		to:       "0",
   835  	},
   836  	{
   837  		// Note: chained-state, we add 1 unit here, but the 3 units
   838  		// from the first condition still exist
   839  		about:    "force the unit onto bootstrap machine",
   840  		expected: []string{"dummy/3"},
   841  		to:       "0",
   842  	},
   843  	{
   844  		about:   "unknown service name",
   845  		service: "unknown-service",
   846  		err:     `service "unknown-service" not found`,
   847  	},
   848  }
   849  
   850  func (s *clientSuite) TestClientAddServiceUnits(c *gc.C) {
   851  	s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   852  	for i, t := range clientAddServiceUnitsTests {
   853  		c.Logf("test %d. %s", i, t.about)
   854  		serviceName := t.service
   855  		if serviceName == "" {
   856  			serviceName = "dummy"
   857  		}
   858  		units, err := s.APIState.Client().AddServiceUnits(serviceName, len(t.expected), t.to)
   859  		if t.err != "" {
   860  			c.Assert(err, gc.ErrorMatches, t.err)
   861  			continue
   862  		}
   863  		c.Assert(err, jc.ErrorIsNil)
   864  		c.Assert(units, gc.DeepEquals, t.expected)
   865  	}
   866  	// Test that we actually assigned the unit to machine 0
   867  	forcedUnit, err := s.BackingState.Unit("dummy/3")
   868  	c.Assert(err, jc.ErrorIsNil)
   869  	assignedMachine, err := forcedUnit.AssignedMachineId()
   870  	c.Assert(err, jc.ErrorIsNil)
   871  	c.Assert(assignedMachine, gc.Equals, "0")
   872  }
   873  
   874  func (s *clientSuite) TestClientAddServiceUnitsToNewContainer(c *gc.C) {
   875  	svc := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   876  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   877  	c.Assert(err, jc.ErrorIsNil)
   878  
   879  	_, err = s.APIState.Client().AddServiceUnits("dummy", 1, "lxc:"+machine.Id())
   880  	c.Assert(err, jc.ErrorIsNil)
   881  
   882  	units, err := svc.AllUnits()
   883  	c.Assert(err, jc.ErrorIsNil)
   884  	mid, err := units[0].AssignedMachineId()
   885  	c.Assert(err, jc.ErrorIsNil)
   886  	c.Assert(mid, gc.Equals, machine.Id()+"/lxc/0")
   887  }
   888  
   889  func (s *clientSuite) assertAddServiceUnits(c *gc.C) {
   890  	units, err := s.APIState.Client().AddServiceUnits("dummy", 3, "")
   891  	c.Assert(err, jc.ErrorIsNil)
   892  	c.Assert(units, gc.DeepEquals, []string{"dummy/0", "dummy/1", "dummy/2"})
   893  
   894  	// Test that we actually assigned the unit to machine 0
   895  	forcedUnit, err := s.BackingState.Unit("dummy/0")
   896  	c.Assert(err, jc.ErrorIsNil)
   897  	assignedMachine, err := forcedUnit.AssignedMachineId()
   898  	c.Assert(err, jc.ErrorIsNil)
   899  	c.Assert(assignedMachine, gc.Equals, "0")
   900  }
   901  
   902  func (s *clientSuite) assertAddServiceUnitsBlocked(c *gc.C, msg string) {
   903  	_, err := s.APIState.Client().AddServiceUnits("dummy", 3, "")
   904  	s.AssertBlocked(c, err, msg)
   905  }
   906  
   907  func (s *clientSuite) TestBlockDestroyAddServiceUnits(c *gc.C) {
   908  	s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   909  	s.BlockDestroyEnvironment(c, "TestBlockDestroyAddServiceUnits")
   910  	s.assertAddServiceUnits(c)
   911  }
   912  
   913  func (s *clientSuite) TestBlockRemoveAddServiceUnits(c *gc.C) {
   914  	s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   915  	s.BlockRemoveObject(c, "TestBlockRemoveAddServiceUnits")
   916  	s.assertAddServiceUnits(c)
   917  }
   918  
   919  func (s *clientSuite) TestBlockChangeAddServiceUnits(c *gc.C) {
   920  	s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   921  	s.BlockAllChanges(c, "TestBlockChangeAddServiceUnits")
   922  	s.assertAddServiceUnitsBlocked(c, "TestBlockChangeAddServiceUnits")
   923  }
   924  
   925  func (s *clientSuite) TestClientAddUnitToMachineNotFound(c *gc.C) {
   926  	s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
   927  	_, err := s.APIState.Client().AddServiceUnits("dummy", 1, "42")
   928  	c.Assert(err, gc.ErrorMatches, `cannot add units for service "dummy" to machine 42: machine 42 not found`)
   929  }
   930  
   931  func (s *clientSuite) TestClientCharmInfo(c *gc.C) {
   932  	var clientCharmInfoTests = []struct {
   933  		about           string
   934  		charm           string
   935  		url             string
   936  		expectedActions *charm.Actions
   937  		err             string
   938  	}{
   939  		{
   940  			about: "dummy charm which contains an expectedActions spec",
   941  			charm: "dummy",
   942  			url:   "local:quantal/dummy-1",
   943  			expectedActions: &charm.Actions{
   944  				ActionSpecs: map[string]charm.ActionSpec{
   945  					"snapshot": {
   946  						Description: "Take a snapshot of the database.",
   947  						Params: map[string]interface{}{
   948  							"type":        "object",
   949  							"title":       "snapshot",
   950  							"description": "Take a snapshot of the database.",
   951  							"properties": map[string]interface{}{
   952  								"outfile": map[string]interface{}{
   953  									"default":     "foo.bz2",
   954  									"description": "The file to write out to.",
   955  									"type":        "string",
   956  								},
   957  							},
   958  						},
   959  					},
   960  				},
   961  			},
   962  		},
   963  		{
   964  			about: "retrieves charm info",
   965  			// Use wordpress for tests so that we can compare Provides and Requires.
   966  			charm: "wordpress",
   967  			expectedActions: &charm.Actions{ActionSpecs: map[string]charm.ActionSpec{
   968  				"fakeaction": {
   969  					Description: "No description",
   970  					Params: map[string]interface{}{
   971  						"type":        "object",
   972  						"title":       "fakeaction",
   973  						"description": "No description",
   974  						"properties":  map[string]interface{}{},
   975  					},
   976  				},
   977  			}},
   978  			url: "local:quantal/wordpress-3",
   979  		},
   980  		{
   981  			about:           "invalid URL",
   982  			charm:           "wordpress",
   983  			expectedActions: &charm.Actions{ActionSpecs: nil},
   984  			url:             "not-valid",
   985  			err:             "charm url series is not resolved",
   986  		},
   987  		{
   988  			about:           "invalid schema",
   989  			charm:           "wordpress",
   990  			expectedActions: &charm.Actions{ActionSpecs: nil},
   991  			url:             "not-valid:your-arguments",
   992  			err:             `charm URL has invalid schema: "not-valid:your-arguments"`,
   993  		},
   994  		{
   995  			about:           "unknown charm",
   996  			charm:           "wordpress",
   997  			expectedActions: &charm.Actions{ActionSpecs: nil},
   998  			url:             "cs:missing/one-1",
   999  			err:             `charm "cs:missing/one-1" not found`,
  1000  		},
  1001  	}
  1002  
  1003  	for i, t := range clientCharmInfoTests {
  1004  		c.Logf("test %d. %s", i, t.about)
  1005  		charm := s.AddTestingCharm(c, t.charm)
  1006  		info, err := s.APIState.Client().CharmInfo(t.url)
  1007  		if t.err != "" {
  1008  			c.Check(err, gc.ErrorMatches, t.err)
  1009  			continue
  1010  		}
  1011  		c.Assert(err, jc.ErrorIsNil)
  1012  		expected := &api.CharmInfo{
  1013  			Revision: charm.Revision(),
  1014  			URL:      charm.URL().String(),
  1015  			Config:   charm.Config(),
  1016  			Meta:     charm.Meta(),
  1017  			Actions:  charm.Actions(),
  1018  		}
  1019  		c.Check(info, jc.DeepEquals, expected)
  1020  		c.Check(info.Actions, jc.DeepEquals, t.expectedActions)
  1021  	}
  1022  }
  1023  
  1024  func (s *clientSuite) TestClientEnvironmentInfo(c *gc.C) {
  1025  	conf, _ := s.State.EnvironConfig()
  1026  	info, err := s.APIState.Client().EnvironmentInfo()
  1027  	c.Assert(err, jc.ErrorIsNil)
  1028  	env, err := s.State.Environment()
  1029  	c.Assert(err, jc.ErrorIsNil)
  1030  	c.Assert(info.DefaultSeries, gc.Equals, config.PreferredSeries(conf))
  1031  	c.Assert(info.ProviderType, gc.Equals, conf.Type())
  1032  	c.Assert(info.Name, gc.Equals, conf.Name())
  1033  	c.Assert(info.UUID, gc.Equals, env.UUID())
  1034  	c.Assert(info.ServerUUID, gc.Equals, env.ServerUUID())
  1035  }
  1036  
  1037  var clientAnnotationsTests = []struct {
  1038  	about    string
  1039  	initial  map[string]string
  1040  	input    map[string]string
  1041  	expected map[string]string
  1042  	err      string
  1043  }{
  1044  	{
  1045  		about:    "test setting an annotation",
  1046  		input:    map[string]string{"mykey": "myvalue"},
  1047  		expected: map[string]string{"mykey": "myvalue"},
  1048  	},
  1049  	{
  1050  		about:    "test setting multiple annotations",
  1051  		input:    map[string]string{"key1": "value1", "key2": "value2"},
  1052  		expected: map[string]string{"key1": "value1", "key2": "value2"},
  1053  	},
  1054  	{
  1055  		about:    "test overriding annotations",
  1056  		initial:  map[string]string{"mykey": "myvalue"},
  1057  		input:    map[string]string{"mykey": "another-value"},
  1058  		expected: map[string]string{"mykey": "another-value"},
  1059  	},
  1060  	{
  1061  		about: "test setting an invalid annotation",
  1062  		input: map[string]string{"invalid.key": "myvalue"},
  1063  		err:   `cannot update annotations on .*: invalid key "invalid.key"`,
  1064  	},
  1065  }
  1066  
  1067  func (s *clientSuite) TestClientAnnotations(c *gc.C) {
  1068  	// Set up entities.
  1069  	service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
  1070  	unit, err := service.AddUnit()
  1071  	c.Assert(err, jc.ErrorIsNil)
  1072  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1073  	c.Assert(err, jc.ErrorIsNil)
  1074  	environment, err := s.State.Environment()
  1075  	c.Assert(err, jc.ErrorIsNil)
  1076  	type taggedAnnotator interface {
  1077  		state.Entity
  1078  	}
  1079  	entities := []taggedAnnotator{service, unit, machine, environment}
  1080  	for i, t := range clientAnnotationsTests {
  1081  		for _, entity := range entities {
  1082  			id := entity.Tag().String() // this is WRONG, it should be Tag().Id() but the code is wrong.
  1083  			c.Logf("test %d. %s. entity %s", i, t.about, id)
  1084  			// Set initial entity annotations.
  1085  			err := s.APIState.Client().SetAnnotations(id, t.initial)
  1086  			c.Assert(err, jc.ErrorIsNil)
  1087  			// Add annotations using the API call.
  1088  			err = s.APIState.Client().SetAnnotations(id, t.input)
  1089  			if t.err != "" {
  1090  				c.Assert(err, gc.ErrorMatches, t.err)
  1091  				continue
  1092  			}
  1093  			// Retrieve annotations using the API call.
  1094  			ann, err := s.APIState.Client().GetAnnotations(id)
  1095  			c.Assert(err, jc.ErrorIsNil)
  1096  			// Check annotations are correctly returned.
  1097  			c.Assert(ann, gc.DeepEquals, t.input)
  1098  			// Clean up annotations on the current entity.
  1099  			cleanup := make(map[string]string)
  1100  			for key := range ann {
  1101  				cleanup[key] = ""
  1102  			}
  1103  			err = s.APIState.Client().SetAnnotations(id, cleanup)
  1104  			c.Assert(err, jc.ErrorIsNil)
  1105  		}
  1106  	}
  1107  }
  1108  
  1109  func (s *clientSuite) TestCharmAnnotationsUnsupported(c *gc.C) {
  1110  	// Set up charm.
  1111  	charm := s.AddTestingCharm(c, "dummy")
  1112  	id := charm.Tag().Id()
  1113  	for i, t := range clientAnnotationsTests {
  1114  		c.Logf("test %d. %s. entity %s", i, t.about, id)
  1115  		// Add annotations using the API call.
  1116  		err := s.APIState.Client().SetAnnotations(id, t.input)
  1117  		// Should not be able to annotate charm with this client
  1118  		c.Assert(err.Error(), gc.Matches, ".*is not a valid tag.*")
  1119  
  1120  		// Retrieve annotations using the API call.
  1121  		ann, err := s.APIState.Client().GetAnnotations(id)
  1122  		// Should not be able to get annotations from charm using this client
  1123  		c.Assert(err.Error(), gc.Matches, ".*is not a valid tag.*")
  1124  		c.Assert(ann, gc.IsNil)
  1125  	}
  1126  }
  1127  
  1128  func (s *clientSuite) TestClientAnnotationsBadEntity(c *gc.C) {
  1129  	bad := []string{"", "machine", "-foo", "foo-", "---", "machine-jim", "unit-123", "unit-foo", "service-", "service-foo/bar"}
  1130  	expected := `".*" is not a valid( [a-z]+)? tag`
  1131  	for _, id := range bad {
  1132  		err := s.APIState.Client().SetAnnotations(id, map[string]string{"mykey": "myvalue"})
  1133  		c.Assert(err, gc.ErrorMatches, expected)
  1134  		_, err = s.APIState.Client().GetAnnotations(id)
  1135  		c.Assert(err, gc.ErrorMatches, expected)
  1136  	}
  1137  }
  1138  
  1139  var serviceExposeTests = []struct {
  1140  	about   string
  1141  	service string
  1142  	err     string
  1143  	exposed bool
  1144  }{
  1145  	{
  1146  		about:   "unknown service name",
  1147  		service: "unknown-service",
  1148  		err:     `service "unknown-service" not found`,
  1149  	},
  1150  	{
  1151  		about:   "expose a service",
  1152  		service: "dummy-service",
  1153  		exposed: true,
  1154  	},
  1155  	{
  1156  		about:   "expose an already exposed service",
  1157  		service: "exposed-service",
  1158  		exposed: true,
  1159  	},
  1160  }
  1161  
  1162  func (s *clientSuite) TestClientServiceExpose(c *gc.C) {
  1163  	charm := s.AddTestingCharm(c, "dummy")
  1164  	serviceNames := []string{"dummy-service", "exposed-service"}
  1165  	svcs := make([]*state.Service, len(serviceNames))
  1166  	var err error
  1167  	for i, name := range serviceNames {
  1168  		svcs[i] = s.AddTestingService(c, name, charm)
  1169  		c.Assert(svcs[i].IsExposed(), jc.IsFalse)
  1170  	}
  1171  	err = svcs[1].SetExposed()
  1172  	c.Assert(err, jc.ErrorIsNil)
  1173  	c.Assert(svcs[1].IsExposed(), jc.IsTrue)
  1174  	for i, t := range serviceExposeTests {
  1175  		c.Logf("test %d. %s", i, t.about)
  1176  		err = s.APIState.Client().ServiceExpose(t.service)
  1177  		if t.err != "" {
  1178  			c.Assert(err, gc.ErrorMatches, t.err)
  1179  		} else {
  1180  			c.Assert(err, jc.ErrorIsNil)
  1181  			service, err := s.State.Service(t.service)
  1182  			c.Assert(err, jc.ErrorIsNil)
  1183  			c.Assert(service.IsExposed(), gc.Equals, t.exposed)
  1184  		}
  1185  	}
  1186  }
  1187  
  1188  func (s *clientSuite) setupServiceExpose(c *gc.C) {
  1189  	charm := s.AddTestingCharm(c, "dummy")
  1190  	serviceNames := []string{"dummy-service", "exposed-service"}
  1191  	svcs := make([]*state.Service, len(serviceNames))
  1192  	var err error
  1193  	for i, name := range serviceNames {
  1194  		svcs[i] = s.AddTestingService(c, name, charm)
  1195  		c.Assert(svcs[i].IsExposed(), jc.IsFalse)
  1196  	}
  1197  	err = svcs[1].SetExposed()
  1198  	c.Assert(err, jc.ErrorIsNil)
  1199  	c.Assert(svcs[1].IsExposed(), jc.IsTrue)
  1200  }
  1201  
  1202  func (s *clientSuite) assertServiceExpose(c *gc.C) {
  1203  	for i, t := range serviceExposeTests {
  1204  		c.Logf("test %d. %s", i, t.about)
  1205  		err := s.APIState.Client().ServiceExpose(t.service)
  1206  		if t.err != "" {
  1207  			c.Assert(err, gc.ErrorMatches, t.err)
  1208  		} else {
  1209  			c.Assert(err, jc.ErrorIsNil)
  1210  			service, err := s.State.Service(t.service)
  1211  			c.Assert(err, jc.ErrorIsNil)
  1212  			c.Assert(service.IsExposed(), gc.Equals, t.exposed)
  1213  		}
  1214  	}
  1215  }
  1216  
  1217  func (s *clientSuite) assertServiceExposeBlocked(c *gc.C, msg string) {
  1218  	for i, t := range serviceExposeTests {
  1219  		c.Logf("test %d. %s", i, t.about)
  1220  		err := s.APIState.Client().ServiceExpose(t.service)
  1221  		s.AssertBlocked(c, err, msg)
  1222  	}
  1223  }
  1224  
  1225  func (s *clientSuite) TestBlockDestroyServiceExpose(c *gc.C) {
  1226  	s.setupServiceExpose(c)
  1227  	s.BlockDestroyEnvironment(c, "TestBlockDestroyServiceExpose")
  1228  	s.assertServiceExpose(c)
  1229  }
  1230  
  1231  func (s *clientSuite) TestBlockRemoveServiceExpose(c *gc.C) {
  1232  	s.setupServiceExpose(c)
  1233  	s.BlockRemoveObject(c, "TestBlockRemoveServiceExpose")
  1234  	s.assertServiceExpose(c)
  1235  }
  1236  
  1237  func (s *clientSuite) TestBlockChangesServiceExpose(c *gc.C) {
  1238  	s.setupServiceExpose(c)
  1239  	s.BlockAllChanges(c, "TestBlockChangesServiceExpose")
  1240  	s.assertServiceExposeBlocked(c, "TestBlockChangesServiceExpose")
  1241  }
  1242  
  1243  var serviceUnexposeTests = []struct {
  1244  	about    string
  1245  	service  string
  1246  	err      string
  1247  	initial  bool
  1248  	expected bool
  1249  }{
  1250  	{
  1251  		about:   "unknown service name",
  1252  		service: "unknown-service",
  1253  		err:     `service "unknown-service" not found`,
  1254  	},
  1255  	{
  1256  		about:    "unexpose a service",
  1257  		service:  "dummy-service",
  1258  		initial:  true,
  1259  		expected: false,
  1260  	},
  1261  	{
  1262  		about:    "unexpose an already unexposed service",
  1263  		service:  "dummy-service",
  1264  		initial:  false,
  1265  		expected: false,
  1266  	},
  1267  }
  1268  
  1269  func (s *clientSuite) TestClientServiceUnexpose(c *gc.C) {
  1270  	charm := s.AddTestingCharm(c, "dummy")
  1271  	for i, t := range serviceUnexposeTests {
  1272  		c.Logf("test %d. %s", i, t.about)
  1273  		svc := s.AddTestingService(c, "dummy-service", charm)
  1274  		if t.initial {
  1275  			svc.SetExposed()
  1276  		}
  1277  		c.Assert(svc.IsExposed(), gc.Equals, t.initial)
  1278  		err := s.APIState.Client().ServiceUnexpose(t.service)
  1279  		if t.err == "" {
  1280  			c.Assert(err, jc.ErrorIsNil)
  1281  			svc.Refresh()
  1282  			c.Assert(svc.IsExposed(), gc.Equals, t.expected)
  1283  		} else {
  1284  			c.Assert(err, gc.ErrorMatches, t.err)
  1285  		}
  1286  		err = svc.Destroy()
  1287  		c.Assert(err, jc.ErrorIsNil)
  1288  	}
  1289  }
  1290  
  1291  func (s *clientSuite) setupServiceUnexpose(c *gc.C) *state.Service {
  1292  	charm := s.AddTestingCharm(c, "dummy")
  1293  	svc := s.AddTestingService(c, "dummy-service", charm)
  1294  	svc.SetExposed()
  1295  	c.Assert(svc.IsExposed(), gc.Equals, true)
  1296  	return svc
  1297  }
  1298  
  1299  func (s *clientSuite) assertServiceUnexpose(c *gc.C, svc *state.Service) {
  1300  	err := s.APIState.Client().ServiceUnexpose("dummy-service")
  1301  	c.Assert(err, jc.ErrorIsNil)
  1302  	svc.Refresh()
  1303  	c.Assert(svc.IsExposed(), gc.Equals, false)
  1304  	err = svc.Destroy()
  1305  	c.Assert(err, jc.ErrorIsNil)
  1306  }
  1307  
  1308  func (s *clientSuite) assertServiceUnexposeBlocked(c *gc.C, svc *state.Service, msg string) {
  1309  	err := s.APIState.Client().ServiceUnexpose("dummy-service")
  1310  	s.AssertBlocked(c, err, msg)
  1311  	err = svc.Destroy()
  1312  	c.Assert(err, jc.ErrorIsNil)
  1313  }
  1314  
  1315  func (s *clientSuite) TestBlockDestroyServiceUnexpose(c *gc.C) {
  1316  	svc := s.setupServiceUnexpose(c)
  1317  	s.BlockDestroyEnvironment(c, "TestBlockDestroyServiceUnexpose")
  1318  	s.assertServiceUnexpose(c, svc)
  1319  }
  1320  
  1321  func (s *clientSuite) TestBlockRemoveServiceUnexpose(c *gc.C) {
  1322  	svc := s.setupServiceUnexpose(c)
  1323  	s.BlockRemoveObject(c, "TestBlockRemoveServiceUnexpose")
  1324  	s.assertServiceUnexpose(c, svc)
  1325  }
  1326  
  1327  func (s *clientSuite) TestBlockChangesServiceUnexpose(c *gc.C) {
  1328  	svc := s.setupServiceUnexpose(c)
  1329  	s.BlockAllChanges(c, "TestBlockChangesServiceUnexpose")
  1330  	s.assertServiceUnexposeBlocked(c, svc, "TestBlockChangesServiceUnexpose")
  1331  }
  1332  
  1333  var serviceDestroyTests = []struct {
  1334  	about   string
  1335  	service string
  1336  	err     string
  1337  }{
  1338  	{
  1339  		about:   "unknown service name",
  1340  		service: "unknown-service",
  1341  		err:     `service "unknown-service" not found`,
  1342  	},
  1343  	{
  1344  		about:   "destroy a service",
  1345  		service: "dummy-service",
  1346  	},
  1347  	{
  1348  		about:   "destroy an already destroyed service",
  1349  		service: "dummy-service",
  1350  		err:     `service "dummy-service" not found`,
  1351  	},
  1352  }
  1353  
  1354  func (s *clientSuite) TestClientServiceDestroy(c *gc.C) {
  1355  	s.AddTestingService(c, "dummy-service", s.AddTestingCharm(c, "dummy"))
  1356  	for i, t := range serviceDestroyTests {
  1357  		c.Logf("test %d. %s", i, t.about)
  1358  		err := s.APIState.Client().ServiceDestroy(t.service)
  1359  		if t.err != "" {
  1360  			c.Assert(err, gc.ErrorMatches, t.err)
  1361  		} else {
  1362  			c.Assert(err, jc.ErrorIsNil)
  1363  		}
  1364  	}
  1365  
  1366  	// Now do ServiceDestroy on a service with units. Destroy will
  1367  	// cause the service to be not-Alive, but will not remove its
  1368  	// document.
  1369  	s.setUpScenario(c)
  1370  	serviceName := "wordpress"
  1371  	service, err := s.State.Service(serviceName)
  1372  	c.Assert(err, jc.ErrorIsNil)
  1373  	err = s.APIState.Client().ServiceDestroy(serviceName)
  1374  	c.Assert(err, jc.ErrorIsNil)
  1375  	err = service.Refresh()
  1376  	c.Assert(err, jc.ErrorIsNil)
  1377  	c.Assert(service.Life(), gc.Not(gc.Equals), state.Alive)
  1378  }
  1379  
  1380  func assertLife(c *gc.C, entity state.Living, life state.Life) {
  1381  	err := entity.Refresh()
  1382  	c.Assert(err, jc.ErrorIsNil)
  1383  	c.Assert(entity.Life(), gc.Equals, life)
  1384  }
  1385  
  1386  func assertRemoved(c *gc.C, entity state.Living) {
  1387  	err := entity.Refresh()
  1388  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1389  }
  1390  
  1391  func assertKill(c *gc.C, killer Killer) {
  1392  	c.Assert(killer.Kill(), gc.IsNil)
  1393  }
  1394  
  1395  func (s *clientSuite) setupDestroyMachinesTest(c *gc.C) (*state.Machine, *state.Machine, *state.Machine, *state.Unit) {
  1396  	m0, err := s.State.AddMachine("quantal", state.JobManageEnviron)
  1397  	c.Assert(err, jc.ErrorIsNil)
  1398  	m1, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1399  	c.Assert(err, jc.ErrorIsNil)
  1400  	m2, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1401  	c.Assert(err, jc.ErrorIsNil)
  1402  
  1403  	sch := s.AddTestingCharm(c, "wordpress")
  1404  	wordpress := s.AddTestingService(c, "wordpress", sch)
  1405  	u, err := wordpress.AddUnit()
  1406  	c.Assert(err, jc.ErrorIsNil)
  1407  	err = u.AssignToMachine(m1)
  1408  	c.Assert(err, jc.ErrorIsNil)
  1409  
  1410  	return m0, m1, m2, u
  1411  }
  1412  
  1413  func (s *clientSuite) TestDestroyMachines(c *gc.C) {
  1414  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
  1415  	s.assertDestroyMachineSuccess(c, u, m0, m1, m2)
  1416  }
  1417  
  1418  func (s *clientSuite) TestForceDestroyMachines(c *gc.C) {
  1419  	s.assertForceDestroyMachines(c)
  1420  }
  1421  
  1422  func (s *clientSuite) TestDestroyPrincipalUnits(c *gc.C) {
  1423  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  1424  	units := make([]*state.Unit, 5)
  1425  	for i := range units {
  1426  		unit, err := wordpress.AddUnit()
  1427  		c.Assert(err, jc.ErrorIsNil)
  1428  		err = unit.SetAgentStatus(state.StatusIdle, "", nil)
  1429  		c.Assert(err, jc.ErrorIsNil)
  1430  		units[i] = unit
  1431  	}
  1432  	s.assertDestroyPrincipalUnits(c, units)
  1433  }
  1434  
  1435  func (s *clientSuite) TestDestroySubordinateUnits(c *gc.C) {
  1436  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  1437  	wordpress0, err := wordpress.AddUnit()
  1438  	c.Assert(err, jc.ErrorIsNil)
  1439  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
  1440  	eps, err := s.State.InferEndpoints("logging", "wordpress")
  1441  	c.Assert(err, jc.ErrorIsNil)
  1442  	rel, err := s.State.AddRelation(eps...)
  1443  	c.Assert(err, jc.ErrorIsNil)
  1444  	ru, err := rel.Unit(wordpress0)
  1445  	c.Assert(err, jc.ErrorIsNil)
  1446  	err = ru.EnterScope(nil)
  1447  	c.Assert(err, jc.ErrorIsNil)
  1448  	logging0, err := s.State.Unit("logging/0")
  1449  	c.Assert(err, jc.ErrorIsNil)
  1450  
  1451  	// Try to destroy the subordinate alone; check it fails.
  1452  	err = s.APIState.Client().DestroyServiceUnits("logging/0")
  1453  	c.Assert(err, gc.ErrorMatches, `no units were destroyed: unit "logging/0" is a subordinate`)
  1454  	assertLife(c, logging0, state.Alive)
  1455  
  1456  	s.assertDestroySubordinateUnits(c, wordpress0, logging0)
  1457  }
  1458  
  1459  func (s *clientSuite) testClientUnitResolved(c *gc.C, retry bool, expectedResolvedMode state.ResolvedMode) {
  1460  	// Setup:
  1461  	s.setUpScenario(c)
  1462  	u, err := s.State.Unit("wordpress/0")
  1463  	c.Assert(err, jc.ErrorIsNil)
  1464  	err = u.SetAgentStatus(state.StatusError, "gaaah", nil)
  1465  	c.Assert(err, jc.ErrorIsNil)
  1466  	// Code under test:
  1467  	err = s.APIState.Client().Resolved("wordpress/0", retry)
  1468  	c.Assert(err, jc.ErrorIsNil)
  1469  	// Freshen the unit's state.
  1470  	err = u.Refresh()
  1471  	c.Assert(err, jc.ErrorIsNil)
  1472  	// And now the actual test assertions: we set the unit as resolved via
  1473  	// the API so it should have a resolved mode set.
  1474  	mode := u.Resolved()
  1475  	c.Assert(mode, gc.Equals, expectedResolvedMode)
  1476  }
  1477  
  1478  func (s *clientSuite) TestClientUnitResolved(c *gc.C) {
  1479  	s.testClientUnitResolved(c, false, state.ResolvedNoHooks)
  1480  }
  1481  
  1482  func (s *clientSuite) TestClientUnitResolvedRetry(c *gc.C) {
  1483  	s.testClientUnitResolved(c, true, state.ResolvedRetryHooks)
  1484  }
  1485  
  1486  func (s *clientSuite) setupResolved(c *gc.C) *state.Unit {
  1487  	s.setUpScenario(c)
  1488  	u, err := s.State.Unit("wordpress/0")
  1489  	c.Assert(err, jc.ErrorIsNil)
  1490  	err = u.SetAgentStatus(state.StatusError, "gaaah", nil)
  1491  	c.Assert(err, jc.ErrorIsNil)
  1492  	return u
  1493  }
  1494  
  1495  func (s *clientSuite) assertResolved(c *gc.C, u *state.Unit) {
  1496  	err := s.APIState.Client().Resolved("wordpress/0", true)
  1497  	c.Assert(err, jc.ErrorIsNil)
  1498  	// Freshen the unit's state.
  1499  	err = u.Refresh()
  1500  	c.Assert(err, jc.ErrorIsNil)
  1501  	// And now the actual test assertions: we set the unit as resolved via
  1502  	// the API so it should have a resolved mode set.
  1503  	mode := u.Resolved()
  1504  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
  1505  }
  1506  
  1507  func (s *clientSuite) assertResolvedBlocked(c *gc.C, u *state.Unit, msg string) {
  1508  	err := s.APIState.Client().Resolved("wordpress/0", true)
  1509  	s.AssertBlocked(c, err, msg)
  1510  }
  1511  
  1512  func (s *clientSuite) TestBlockDestroyUnitResolved(c *gc.C) {
  1513  	u := s.setupResolved(c)
  1514  	s.BlockDestroyEnvironment(c, "TestBlockDestroyUnitResolved")
  1515  	s.assertResolved(c, u)
  1516  }
  1517  
  1518  func (s *clientSuite) TestBlockRemoveUnitResolved(c *gc.C) {
  1519  	u := s.setupResolved(c)
  1520  	s.BlockRemoveObject(c, "TestBlockRemoveUnitResolved")
  1521  	s.assertResolved(c, u)
  1522  }
  1523  
  1524  func (s *clientSuite) TestBlockChangeUnitResolved(c *gc.C) {
  1525  	u := s.setupResolved(c)
  1526  	s.BlockAllChanges(c, "TestBlockChangeUnitResolved")
  1527  	s.assertResolvedBlocked(c, u, "TestBlockChangeUnitResolved")
  1528  }
  1529  
  1530  type clientRepoSuite struct {
  1531  	baseSuite
  1532  	apiservertesting.CharmStoreSuite
  1533  }
  1534  
  1535  var _ = gc.Suite(&clientRepoSuite{})
  1536  
  1537  func (s *clientRepoSuite) SetUpSuite(c *gc.C) {
  1538  	s.CharmStoreSuite.SetUpSuite(c)
  1539  	s.baseSuite.SetUpSuite(c)
  1540  }
  1541  
  1542  func (s *clientRepoSuite) TearDownSuite(c *gc.C) {
  1543  	s.CharmStoreSuite.TearDownSuite(c)
  1544  	s.baseSuite.TearDownSuite(c)
  1545  }
  1546  
  1547  func (s *clientRepoSuite) SetUpTest(c *gc.C) {
  1548  	s.baseSuite.SetUpTest(c)
  1549  	s.CharmStoreSuite.Session = s.baseSuite.Session
  1550  	s.CharmStoreSuite.SetUpTest(c)
  1551  }
  1552  
  1553  func (s *clientRepoSuite) TearDownTest(c *gc.C) {
  1554  	s.CharmStoreSuite.TearDownTest(c)
  1555  	s.baseSuite.TearDownTest(c)
  1556  }
  1557  
  1558  func (s *clientRepoSuite) TestClientServiceDeployCharmErrors(c *gc.C) {
  1559  	for url, expect := range map[string]string{
  1560  		"wordpress":                   "charm url series is not resolved",
  1561  		"cs:wordpress":                "charm url series is not resolved",
  1562  		"cs:precise/wordpress":        "charm url must include revision",
  1563  		"cs:precise/wordpress-999999": `.* charm "cs:precise/wordpress-999999".* not found`,
  1564  	} {
  1565  		c.Logf("test %s", url)
  1566  		err := s.APIState.Client().ServiceDeploy(
  1567  			url, "service", 1, "", constraints.Value{}, "",
  1568  		)
  1569  		c.Check(err, gc.ErrorMatches, expect)
  1570  		_, err = s.State.Service("service")
  1571  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1572  	}
  1573  }
  1574  
  1575  func (s *clientRepoSuite) TestClientServiceDeployWithNetworks(c *gc.C) {
  1576  	curl, ch := s.UploadCharm(c, "precise/dummy-0", "dummy")
  1577  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  1578  	c.Assert(err, jc.ErrorIsNil)
  1579  	cons := constraints.MustParse("mem=4G networks=^net3")
  1580  
  1581  	// Check for invalid network tags handling.
  1582  	err = s.APIState.Client().ServiceDeployWithNetworks(
  1583  		curl.String(), "service", 3, "", cons, "",
  1584  		[]string{"net1", "net2"},
  1585  	)
  1586  	c.Assert(err, gc.ErrorMatches, `"net1" is not a valid tag`)
  1587  
  1588  	err = s.APIState.Client().ServiceDeployWithNetworks(
  1589  		curl.String(), "service", 3, "", cons, "",
  1590  		[]string{"network-net1", "network-net2"},
  1591  	)
  1592  	c.Assert(err, jc.ErrorIsNil)
  1593  	service := apiservertesting.AssertPrincipalServiceDeployed(c, s.State, "service", curl, false, ch, cons)
  1594  
  1595  	networks, err := service.Networks()
  1596  	c.Assert(err, jc.ErrorIsNil)
  1597  	c.Assert(networks, gc.DeepEquals, []string{"net1", "net2"})
  1598  	serviceCons, err := service.Constraints()
  1599  	c.Assert(err, jc.ErrorIsNil)
  1600  	c.Assert(serviceCons, gc.DeepEquals, cons)
  1601  }
  1602  
  1603  func (s *clientRepoSuite) setupServiceDeploy(c *gc.C, args string) (*charm.URL, charm.Charm, constraints.Value) {
  1604  	curl, ch := s.UploadCharm(c, "precise/dummy-42", "dummy")
  1605  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  1606  	c.Assert(err, jc.ErrorIsNil)
  1607  	cons := constraints.MustParse(args)
  1608  	return curl, ch, cons
  1609  }
  1610  
  1611  func (s *clientRepoSuite) assertServiceDeployWithNetworks(c *gc.C, curl *charm.URL, ch charm.Charm, cons constraints.Value) {
  1612  	err := s.APIState.Client().ServiceDeployWithNetworks(
  1613  		curl.String(), "service", 3, "", cons, "",
  1614  		[]string{"network-net1", "network-net2"},
  1615  	)
  1616  	c.Assert(err, jc.ErrorIsNil)
  1617  	service := apiservertesting.AssertPrincipalServiceDeployed(c, s.State, "service", curl, false, ch, cons)
  1618  	networks, err := service.Networks()
  1619  	c.Assert(err, jc.ErrorIsNil)
  1620  	c.Assert(networks, gc.DeepEquals, []string{"net1", "net2"})
  1621  	serviceCons, err := service.Constraints()
  1622  	c.Assert(err, jc.ErrorIsNil)
  1623  	c.Assert(serviceCons, gc.DeepEquals, cons)
  1624  }
  1625  
  1626  func (s *clientRepoSuite) assertServiceDeployWithNetworksBlocked(c *gc.C, msg string, curl *charm.URL, cons constraints.Value) {
  1627  	err := s.APIState.Client().ServiceDeployWithNetworks(
  1628  		curl.String(), "service", 3, "", cons, "",
  1629  		[]string{"network-net1", "network-net2"},
  1630  	)
  1631  	s.AssertBlocked(c, err, msg)
  1632  }
  1633  
  1634  func (s *clientRepoSuite) TestBlockDestroyServiceDeployWithNetworks(c *gc.C) {
  1635  	curl, ch, cons := s.setupServiceDeploy(c, "mem=4G networks=^net3")
  1636  	s.BlockDestroyEnvironment(c, "TestBlockDestroyServiceDeployWithNetworks")
  1637  	s.assertServiceDeployWithNetworks(c, curl, ch, cons)
  1638  }
  1639  
  1640  func (s *clientRepoSuite) TestBlockRemoveServiceDeployWithNetworks(c *gc.C) {
  1641  	curl, ch, cons := s.setupServiceDeploy(c, "mem=4G networks=^net3")
  1642  	s.BlockRemoveObject(c, "TestBlockRemoveServiceDeployWithNetworks")
  1643  	s.assertServiceDeployWithNetworks(c, curl, ch, cons)
  1644  }
  1645  
  1646  func (s *clientRepoSuite) TestBlockChangeServiceDeployWithNetworks(c *gc.C) {
  1647  	curl, _, cons := s.setupServiceDeploy(c, "mem=4G networks=^net3")
  1648  	s.BlockAllChanges(c, "TestBlockChangeServiceDeployWithNetworks")
  1649  	s.assertServiceDeployWithNetworksBlocked(c, "TestBlockChangeServiceDeployWithNetworks", curl, cons)
  1650  }
  1651  
  1652  func (s *clientRepoSuite) TestClientServiceDeployPrincipal(c *gc.C) {
  1653  	// TODO(fwereade): test ToMachineSpec directly on srvClient, when we
  1654  	// manage to extract it as a package and can thus do it conveniently.
  1655  	curl, ch := s.UploadCharm(c, "trusty/dummy-1", "dummy")
  1656  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  1657  	c.Assert(err, jc.ErrorIsNil)
  1658  	mem4g := constraints.MustParse("mem=4G")
  1659  	err = s.APIState.Client().ServiceDeploy(
  1660  		curl.String(), "service", 3, "", mem4g, "",
  1661  	)
  1662  	c.Assert(err, jc.ErrorIsNil)
  1663  	apiservertesting.AssertPrincipalServiceDeployed(c, s.State, "service", curl, false, ch, mem4g)
  1664  }
  1665  
  1666  func (s *clientRepoSuite) assertServiceDeployPrincipal(c *gc.C, curl *charm.URL, ch charm.Charm, mem4g constraints.Value) {
  1667  	err := s.APIState.Client().ServiceDeploy(
  1668  		curl.String(), "service", 3, "", mem4g, "",
  1669  	)
  1670  	c.Assert(err, jc.ErrorIsNil)
  1671  	apiservertesting.AssertPrincipalServiceDeployed(c, s.State, "service", curl, false, ch, mem4g)
  1672  }
  1673  
  1674  func (s *clientRepoSuite) assertServiceDeployPrincipalBlocked(c *gc.C, msg string, curl *charm.URL, mem4g constraints.Value) {
  1675  	err := s.APIState.Client().ServiceDeploy(
  1676  		curl.String(), "service", 3, "", mem4g, "",
  1677  	)
  1678  	s.AssertBlocked(c, err, msg)
  1679  }
  1680  
  1681  func (s *clientRepoSuite) TestBlockDestroyServiceDeployPrincipal(c *gc.C) {
  1682  	curl, bundle, cons := s.setupServiceDeploy(c, "mem=4G")
  1683  	s.BlockDestroyEnvironment(c, "TestBlockDestroyServiceDeployPrincipal")
  1684  	s.assertServiceDeployPrincipal(c, curl, bundle, cons)
  1685  }
  1686  
  1687  func (s *clientRepoSuite) TestBlockRemoveServiceDeployPrincipal(c *gc.C) {
  1688  	curl, bundle, cons := s.setupServiceDeploy(c, "mem=4G")
  1689  	s.BlockRemoveObject(c, "TestBlockRemoveServiceDeployPrincipal")
  1690  	s.assertServiceDeployPrincipal(c, curl, bundle, cons)
  1691  }
  1692  
  1693  func (s *clientRepoSuite) TestBlockChangesServiceDeployPrincipal(c *gc.C) {
  1694  	curl, _, cons := s.setupServiceDeploy(c, "mem=4G")
  1695  	s.BlockAllChanges(c, "TestBlockChangesServiceDeployPrincipal")
  1696  	s.assertServiceDeployPrincipalBlocked(c, "TestBlockChangesServiceDeployPrincipal", curl, cons)
  1697  }
  1698  
  1699  func (s *clientRepoSuite) TestClientServiceDeploySubordinate(c *gc.C) {
  1700  	curl, ch := s.UploadCharm(c, "utopic/logging-47", "logging")
  1701  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  1702  	c.Assert(err, jc.ErrorIsNil)
  1703  	err = s.APIState.Client().ServiceDeploy(
  1704  		curl.String(), "service-name", 0, "", constraints.Value{}, "",
  1705  	)
  1706  	service, err := s.State.Service("service-name")
  1707  	c.Assert(err, jc.ErrorIsNil)
  1708  	charm, force, err := service.Charm()
  1709  	c.Assert(err, jc.ErrorIsNil)
  1710  	c.Assert(force, jc.IsFalse)
  1711  	c.Assert(charm.URL(), gc.DeepEquals, curl)
  1712  	c.Assert(charm.Meta(), gc.DeepEquals, ch.Meta())
  1713  	c.Assert(charm.Config(), gc.DeepEquals, ch.Config())
  1714  
  1715  	units, err := service.AllUnits()
  1716  	c.Assert(err, jc.ErrorIsNil)
  1717  	c.Assert(units, gc.HasLen, 0)
  1718  }
  1719  
  1720  func (s *clientRepoSuite) TestClientServiceDeployConfig(c *gc.C) {
  1721  	// TODO(fwereade): test Config/ConfigYAML handling directly on srvClient.
  1722  	// Can't be done cleanly until it's extracted similarly to Machiner.
  1723  	curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy")
  1724  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  1725  	c.Assert(err, jc.ErrorIsNil)
  1726  	err = s.APIState.Client().ServiceDeploy(
  1727  		curl.String(), "service-name", 1, "service-name:\n  username: fred", constraints.Value{}, "",
  1728  	)
  1729  	c.Assert(err, jc.ErrorIsNil)
  1730  	service, err := s.State.Service("service-name")
  1731  	c.Assert(err, jc.ErrorIsNil)
  1732  	settings, err := service.ConfigSettings()
  1733  	c.Assert(err, jc.ErrorIsNil)
  1734  	c.Assert(settings, gc.DeepEquals, charm.Settings{"username": "fred"})
  1735  }
  1736  
  1737  func (s *clientRepoSuite) TestClientServiceDeployConfigError(c *gc.C) {
  1738  	// TODO(fwereade): test Config/ConfigYAML handling directly on srvClient.
  1739  	// Can't be done cleanly until it's extracted similarly to Machiner.
  1740  	curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy")
  1741  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  1742  	c.Assert(err, jc.ErrorIsNil)
  1743  	err = s.APIState.Client().ServiceDeploy(
  1744  		curl.String(), "service-name", 1, "service-name:\n  skill-level: fred", constraints.Value{}, "",
  1745  	)
  1746  	c.Assert(err, gc.ErrorMatches, `option "skill-level" expected int, got "fred"`)
  1747  	_, err = s.State.Service("service-name")
  1748  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1749  }
  1750  
  1751  func (s *clientRepoSuite) TestClientServiceDeployToMachine(c *gc.C) {
  1752  	curl, ch := s.UploadCharm(c, "precise/dummy-0", "dummy")
  1753  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  1754  	c.Assert(err, jc.ErrorIsNil)
  1755  
  1756  	machine, err := s.State.AddMachine("precise", state.JobHostUnits)
  1757  	c.Assert(err, jc.ErrorIsNil)
  1758  	err = s.APIState.Client().ServiceDeploy(
  1759  		curl.String(), "service-name", 1, "service-name:\n  username: fred", constraints.Value{}, machine.Id(),
  1760  	)
  1761  	c.Assert(err, jc.ErrorIsNil)
  1762  
  1763  	service, err := s.State.Service("service-name")
  1764  	c.Assert(err, jc.ErrorIsNil)
  1765  	charm, force, err := service.Charm()
  1766  	c.Assert(err, jc.ErrorIsNil)
  1767  	c.Assert(force, jc.IsFalse)
  1768  	c.Assert(charm.URL(), gc.DeepEquals, curl)
  1769  	c.Assert(charm.Meta(), gc.DeepEquals, ch.Meta())
  1770  	c.Assert(charm.Config(), gc.DeepEquals, ch.Config())
  1771  
  1772  	units, err := service.AllUnits()
  1773  	c.Assert(err, jc.ErrorIsNil)
  1774  	c.Assert(units, gc.HasLen, 1)
  1775  	mid, err := units[0].AssignedMachineId()
  1776  	c.Assert(err, jc.ErrorIsNil)
  1777  	c.Assert(mid, gc.Equals, machine.Id())
  1778  }
  1779  
  1780  func (s *clientSuite) TestClientServiceDeployToMachineNotFound(c *gc.C) {
  1781  	err := s.APIState.Client().ServiceDeploy(
  1782  		"cs:precise/service-name-1", "service-name", 1, "", constraints.Value{}, "42",
  1783  	)
  1784  	c.Assert(err, gc.ErrorMatches, `cannot deploy "service-name" to machine 42: machine 42 not found`)
  1785  
  1786  	_, err = s.State.Service("service-name")
  1787  	c.Assert(err, gc.ErrorMatches, `service "service-name" not found`)
  1788  }
  1789  
  1790  func (s *clientRepoSuite) TestClientServiceDeployServiceOwner(c *gc.C) {
  1791  	curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy")
  1792  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  1793  	c.Assert(err, jc.ErrorIsNil)
  1794  
  1795  	user := s.Factory.MakeUser(c, &factory.UserParams{Password: "password"})
  1796  	s.APIState = s.OpenAPIAs(c, user.Tag(), "password")
  1797  
  1798  	err = s.APIState.Client().ServiceDeploy(
  1799  		curl.String(), "service", 3, "", constraints.Value{}, "",
  1800  	)
  1801  	c.Assert(err, jc.ErrorIsNil)
  1802  
  1803  	service, err := s.State.Service("service")
  1804  	c.Assert(err, jc.ErrorIsNil)
  1805  	c.Assert(service.GetOwnerTag(), gc.Equals, user.Tag().String())
  1806  }
  1807  
  1808  func (s *clientRepoSuite) deployServiceForTests(c *gc.C) {
  1809  	curl, _ := s.UploadCharm(c, "precise/dummy-1", "dummy")
  1810  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  1811  	c.Assert(err, jc.ErrorIsNil)
  1812  	err = s.APIState.Client().ServiceDeploy(curl.String(),
  1813  		"service", 1, "", constraints.Value{}, "",
  1814  	)
  1815  	c.Assert(err, jc.ErrorIsNil)
  1816  }
  1817  
  1818  func (s *clientRepoSuite) checkClientServiceUpdateSetCharm(c *gc.C, forceCharmUrl bool) {
  1819  	s.deployServiceForTests(c)
  1820  	s.UploadCharm(c, "precise/wordpress-3", "wordpress")
  1821  
  1822  	// Update the charm for the service.
  1823  	args := params.ServiceUpdate{
  1824  		ServiceName:   "service",
  1825  		CharmUrl:      "cs:precise/wordpress-3",
  1826  		ForceCharmUrl: forceCharmUrl,
  1827  	}
  1828  	err := s.APIState.Client().ServiceUpdate(args)
  1829  	c.Assert(err, jc.ErrorIsNil)
  1830  
  1831  	// Ensure the charm has been updated and and the force flag correctly set.
  1832  	service, err := s.State.Service("service")
  1833  	c.Assert(err, jc.ErrorIsNil)
  1834  	ch, force, err := service.Charm()
  1835  	c.Assert(err, jc.ErrorIsNil)
  1836  	c.Assert(ch.URL().String(), gc.Equals, "cs:precise/wordpress-3")
  1837  	c.Assert(force, gc.Equals, forceCharmUrl)
  1838  }
  1839  
  1840  func (s *clientRepoSuite) TestClientServiceUpdateSetCharm(c *gc.C) {
  1841  	s.checkClientServiceUpdateSetCharm(c, false)
  1842  }
  1843  
  1844  func (s *clientRepoSuite) TestBlockDestroyServiceUpdate(c *gc.C) {
  1845  	s.BlockDestroyEnvironment(c, "TestBlockDestroyServiceUpdate")
  1846  	s.checkClientServiceUpdateSetCharm(c, false)
  1847  }
  1848  
  1849  func (s *clientRepoSuite) TestBlockRemoveServiceUpdate(c *gc.C) {
  1850  	s.BlockRemoveObject(c, "TestBlockRemoveServiceUpdate")
  1851  	s.checkClientServiceUpdateSetCharm(c, false)
  1852  }
  1853  
  1854  func (s *clientRepoSuite) setupServiceUpdate(c *gc.C) {
  1855  	s.deployServiceForTests(c)
  1856  	s.UploadCharm(c, "precise/wordpress-3", "wordpress")
  1857  }
  1858  
  1859  func (s *clientRepoSuite) TestBlockChangeServiceUpdate(c *gc.C) {
  1860  	s.setupServiceUpdate(c)
  1861  	s.BlockAllChanges(c, "TestBlockChangeServiceUpdate")
  1862  	// Update the charm for the service.
  1863  	args := params.ServiceUpdate{
  1864  		ServiceName:   "service",
  1865  		CharmUrl:      "cs:precise/wordpress-3",
  1866  		ForceCharmUrl: false,
  1867  	}
  1868  	err := s.APIState.Client().ServiceUpdate(args)
  1869  	s.AssertBlocked(c, err, "TestBlockChangeServiceUpdate")
  1870  }
  1871  
  1872  func (s *clientRepoSuite) TestClientServiceUpdateForceSetCharm(c *gc.C) {
  1873  	s.checkClientServiceUpdateSetCharm(c, true)
  1874  }
  1875  
  1876  func (s *clientRepoSuite) TestBlockServiceUpdateForced(c *gc.C) {
  1877  	s.setupServiceUpdate(c)
  1878  
  1879  	// block all changes. Force should ignore block :)
  1880  	s.BlockAllChanges(c, "TestBlockServiceUpdateForced")
  1881  	s.BlockDestroyEnvironment(c, "TestBlockServiceUpdateForced")
  1882  	s.BlockRemoveObject(c, "TestBlockServiceUpdateForced")
  1883  
  1884  	// Update the charm for the service.
  1885  	args := params.ServiceUpdate{
  1886  		ServiceName:   "service",
  1887  		CharmUrl:      "cs:precise/wordpress-3",
  1888  		ForceCharmUrl: true,
  1889  	}
  1890  	err := s.APIState.Client().ServiceUpdate(args)
  1891  	c.Assert(err, jc.ErrorIsNil)
  1892  
  1893  	// Ensure the charm has been updated and and the force flag correctly set.
  1894  	service, err := s.State.Service("service")
  1895  	c.Assert(err, jc.ErrorIsNil)
  1896  	ch, force, err := service.Charm()
  1897  	c.Assert(err, jc.ErrorIsNil)
  1898  	c.Assert(ch.URL().String(), gc.Equals, "cs:precise/wordpress-3")
  1899  	c.Assert(force, jc.IsTrue)
  1900  }
  1901  
  1902  func (s *clientRepoSuite) TestClientServiceUpdateSetCharmErrors(c *gc.C) {
  1903  	s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  1904  	for charmUrl, expect := range map[string]string{
  1905  		"wordpress":                   "charm url series is not resolved",
  1906  		"cs:wordpress":                "charm url series is not resolved",
  1907  		"cs:precise/wordpress":        "charm url must include revision",
  1908  		"cs:precise/wordpress-999999": `cannot retrieve charm "cs:precise/wordpress-999999": charm not found`,
  1909  	} {
  1910  		c.Logf("test %s", charmUrl)
  1911  		args := params.ServiceUpdate{
  1912  			ServiceName: "wordpress",
  1913  			CharmUrl:    charmUrl,
  1914  		}
  1915  		err := s.APIState.Client().ServiceUpdate(args)
  1916  		c.Check(err, gc.ErrorMatches, expect)
  1917  	}
  1918  }
  1919  
  1920  func (s *clientSuite) TestClientServiceUpdateSetMinUnits(c *gc.C) {
  1921  	service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
  1922  
  1923  	// Set minimum units for the service.
  1924  	minUnits := 2
  1925  	args := params.ServiceUpdate{
  1926  		ServiceName: "dummy",
  1927  		MinUnits:    &minUnits,
  1928  	}
  1929  	err := s.APIState.Client().ServiceUpdate(args)
  1930  	c.Assert(err, jc.ErrorIsNil)
  1931  
  1932  	// Ensure the minimum number of units has been set.
  1933  	c.Assert(service.Refresh(), gc.IsNil)
  1934  	c.Assert(service.MinUnits(), gc.Equals, minUnits)
  1935  }
  1936  
  1937  func (s *clientSuite) TestClientServiceUpdateSetMinUnitsError(c *gc.C) {
  1938  	service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
  1939  
  1940  	// Set a negative minimum number of units for the service.
  1941  	minUnits := -1
  1942  	args := params.ServiceUpdate{
  1943  		ServiceName: "dummy",
  1944  		MinUnits:    &minUnits,
  1945  	}
  1946  	err := s.APIState.Client().ServiceUpdate(args)
  1947  	c.Assert(err, gc.ErrorMatches,
  1948  		`cannot set minimum units for service "dummy": cannot set a negative minimum number of units`)
  1949  
  1950  	// Ensure the minimum number of units has not been set.
  1951  	c.Assert(service.Refresh(), gc.IsNil)
  1952  	c.Assert(service.MinUnits(), gc.Equals, 0)
  1953  }
  1954  
  1955  func (s *clientSuite) TestClientServiceUpdateSetSettingsStrings(c *gc.C) {
  1956  	service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
  1957  
  1958  	// Update settings for the service.
  1959  	args := params.ServiceUpdate{
  1960  		ServiceName:     "dummy",
  1961  		SettingsStrings: map[string]string{"title": "s-title", "username": "s-user"},
  1962  	}
  1963  	err := s.APIState.Client().ServiceUpdate(args)
  1964  	c.Assert(err, jc.ErrorIsNil)
  1965  
  1966  	// Ensure the settings have been correctly updated.
  1967  	expected := charm.Settings{"title": "s-title", "username": "s-user"}
  1968  	obtained, err := service.ConfigSettings()
  1969  	c.Assert(err, jc.ErrorIsNil)
  1970  	c.Assert(obtained, gc.DeepEquals, expected)
  1971  }
  1972  
  1973  func (s *clientSuite) TestClientServiceUpdateSetSettingsYAML(c *gc.C) {
  1974  	service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
  1975  
  1976  	// Update settings for the service.
  1977  	args := params.ServiceUpdate{
  1978  		ServiceName:  "dummy",
  1979  		SettingsYAML: "dummy:\n  title: y-title\n  username: y-user",
  1980  	}
  1981  	err := s.APIState.Client().ServiceUpdate(args)
  1982  	c.Assert(err, jc.ErrorIsNil)
  1983  
  1984  	// Ensure the settings have been correctly updated.
  1985  	expected := charm.Settings{"title": "y-title", "username": "y-user"}
  1986  	obtained, err := service.ConfigSettings()
  1987  	c.Assert(err, jc.ErrorIsNil)
  1988  	c.Assert(obtained, gc.DeepEquals, expected)
  1989  }
  1990  
  1991  func (s *clientSuite) TestClientServiceUpdateSetConstraints(c *gc.C) {
  1992  	service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
  1993  
  1994  	// Update constraints for the service.
  1995  	cons, err := constraints.Parse("mem=4096", "cpu-cores=2")
  1996  	c.Assert(err, jc.ErrorIsNil)
  1997  	args := params.ServiceUpdate{
  1998  		ServiceName: "dummy",
  1999  		Constraints: &cons,
  2000  	}
  2001  	err = s.APIState.Client().ServiceUpdate(args)
  2002  	c.Assert(err, jc.ErrorIsNil)
  2003  
  2004  	// Ensure the constraints have been correctly updated.
  2005  	obtained, err := service.Constraints()
  2006  	c.Assert(err, jc.ErrorIsNil)
  2007  	c.Assert(obtained, gc.DeepEquals, cons)
  2008  }
  2009  
  2010  func (s *clientRepoSuite) TestClientServiceUpdateAllParams(c *gc.C) {
  2011  	s.deployServiceForTests(c)
  2012  	s.UploadCharm(c, "precise/wordpress-3", "wordpress")
  2013  
  2014  	// Update all the service attributes.
  2015  	minUnits := 3
  2016  	cons, err := constraints.Parse("mem=4096", "cpu-cores=2")
  2017  	c.Assert(err, jc.ErrorIsNil)
  2018  	args := params.ServiceUpdate{
  2019  		ServiceName:     "service",
  2020  		CharmUrl:        "cs:precise/wordpress-3",
  2021  		ForceCharmUrl:   true,
  2022  		MinUnits:        &minUnits,
  2023  		SettingsStrings: map[string]string{"blog-title": "string-title"},
  2024  		SettingsYAML:    "service:\n  blog-title: yaml-title\n",
  2025  		Constraints:     &cons,
  2026  	}
  2027  	err = s.APIState.Client().ServiceUpdate(args)
  2028  	c.Assert(err, jc.ErrorIsNil)
  2029  
  2030  	// Ensure the service has been correctly updated.
  2031  	service, err := s.State.Service("service")
  2032  	c.Assert(err, jc.ErrorIsNil)
  2033  
  2034  	// Check the charm.
  2035  	ch, force, err := service.Charm()
  2036  	c.Assert(err, jc.ErrorIsNil)
  2037  	c.Assert(ch.URL().String(), gc.Equals, "cs:precise/wordpress-3")
  2038  	c.Assert(force, jc.IsTrue)
  2039  
  2040  	// Check the minimum number of units.
  2041  	c.Assert(service.MinUnits(), gc.Equals, minUnits)
  2042  
  2043  	// Check the settings: also ensure the YAML settings take precedence
  2044  	// over strings ones.
  2045  	expectedSettings := charm.Settings{"blog-title": "yaml-title"}
  2046  	obtainedSettings, err := service.ConfigSettings()
  2047  	c.Assert(err, jc.ErrorIsNil)
  2048  	c.Assert(obtainedSettings, gc.DeepEquals, expectedSettings)
  2049  
  2050  	// Check the constraints.
  2051  	obtainedConstraints, err := service.Constraints()
  2052  	c.Assert(err, jc.ErrorIsNil)
  2053  	c.Assert(obtainedConstraints, gc.DeepEquals, cons)
  2054  }
  2055  
  2056  func (s *clientSuite) TestClientServiceUpdateNoParams(c *gc.C) {
  2057  	s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  2058  
  2059  	// Calling ServiceUpdate with no parameters set is a no-op.
  2060  	args := params.ServiceUpdate{ServiceName: "wordpress"}
  2061  	err := s.APIState.Client().ServiceUpdate(args)
  2062  	c.Assert(err, jc.ErrorIsNil)
  2063  }
  2064  
  2065  func (s *clientSuite) TestClientServiceUpdateNoService(c *gc.C) {
  2066  	err := s.APIState.Client().ServiceUpdate(params.ServiceUpdate{})
  2067  	c.Assert(err, gc.ErrorMatches, `"" is not a valid service name`)
  2068  }
  2069  
  2070  func (s *clientSuite) TestClientServiceUpdateInvalidService(c *gc.C) {
  2071  	args := params.ServiceUpdate{ServiceName: "no-such-service"}
  2072  	err := s.APIState.Client().ServiceUpdate(args)
  2073  	c.Assert(err, gc.ErrorMatches, `service "no-such-service" not found`)
  2074  }
  2075  
  2076  func (s *clientRepoSuite) TestClientServiceSetCharm(c *gc.C) {
  2077  	curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy")
  2078  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  2079  	c.Assert(err, jc.ErrorIsNil)
  2080  	err = s.APIState.Client().ServiceDeploy(
  2081  		curl.String(), "service", 3, "", constraints.Value{}, "",
  2082  	)
  2083  	c.Assert(err, jc.ErrorIsNil)
  2084  	s.UploadCharm(c, "precise/wordpress-3", "wordpress")
  2085  	err = s.APIState.Client().ServiceSetCharm(
  2086  		"service", "cs:precise/wordpress-3", false,
  2087  	)
  2088  	c.Assert(err, jc.ErrorIsNil)
  2089  
  2090  	// Ensure that the charm is not marked as forced.
  2091  	service, err := s.State.Service("service")
  2092  	c.Assert(err, jc.ErrorIsNil)
  2093  	charm, force, err := service.Charm()
  2094  	c.Assert(err, jc.ErrorIsNil)
  2095  	c.Assert(charm.URL().String(), gc.Equals, "cs:precise/wordpress-3")
  2096  	c.Assert(force, jc.IsFalse)
  2097  }
  2098  
  2099  func (s *clientRepoSuite) setupServiceSetCharm(c *gc.C) {
  2100  	curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy")
  2101  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  2102  	c.Assert(err, jc.ErrorIsNil)
  2103  	err = s.APIState.Client().ServiceDeploy(
  2104  		curl.String(), "service", 3, "", constraints.Value{}, "",
  2105  	)
  2106  	c.Assert(err, jc.ErrorIsNil)
  2107  	s.UploadCharm(c, "precise/wordpress-3", "wordpress")
  2108  }
  2109  
  2110  func (s *clientRepoSuite) assertServiceSetCharm(c *gc.C, force bool) {
  2111  	err := s.APIState.Client().ServiceSetCharm(
  2112  		"service", "cs:precise/wordpress-3", force,
  2113  	)
  2114  	c.Assert(err, jc.ErrorIsNil)
  2115  	// Ensure that the charm is not marked as forced.
  2116  	service, err := s.State.Service("service")
  2117  	c.Assert(err, jc.ErrorIsNil)
  2118  	charm, _, err := service.Charm()
  2119  	c.Assert(err, jc.ErrorIsNil)
  2120  	c.Assert(charm.URL().String(), gc.Equals, "cs:precise/wordpress-3")
  2121  }
  2122  
  2123  func (s *clientRepoSuite) assertServiceSetCharmBlocked(c *gc.C, force bool, msg string) {
  2124  	err := s.APIState.Client().ServiceSetCharm(
  2125  		"service", "cs:precise/wordpress-3", force,
  2126  	)
  2127  	s.AssertBlocked(c, err, msg)
  2128  }
  2129  
  2130  func (s *clientRepoSuite) TestBlockDestroyServiceSetCharm(c *gc.C) {
  2131  	s.setupServiceSetCharm(c)
  2132  	s.BlockDestroyEnvironment(c, "TestBlockDestroyServiceSetCharm")
  2133  	s.assertServiceSetCharm(c, false)
  2134  }
  2135  
  2136  func (s *clientRepoSuite) TestBlockRemoveServiceSetCharm(c *gc.C) {
  2137  	s.setupServiceSetCharm(c)
  2138  	s.BlockRemoveObject(c, "TestBlockRemoveServiceSetCharm")
  2139  	s.assertServiceSetCharm(c, false)
  2140  }
  2141  
  2142  func (s *clientRepoSuite) TestBlockChangesServiceSetCharm(c *gc.C) {
  2143  	s.setupServiceSetCharm(c)
  2144  	s.BlockAllChanges(c, "TestBlockChangesServiceSetCharm")
  2145  	s.assertServiceSetCharmBlocked(c, false, "TestBlockChangesServiceSetCharm")
  2146  }
  2147  
  2148  func (s *clientRepoSuite) TestClientServiceSetCharmForce(c *gc.C) {
  2149  	curl, _ := s.UploadCharm(c, "precise/dummy-0", "dummy")
  2150  	err := service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  2151  	c.Assert(err, jc.ErrorIsNil)
  2152  	err = s.APIState.Client().ServiceDeploy(
  2153  		curl.String(), "service", 3, "", constraints.Value{}, "",
  2154  	)
  2155  	c.Assert(err, jc.ErrorIsNil)
  2156  	s.UploadCharm(c, "precise/wordpress-3", "wordpress")
  2157  	err = s.APIState.Client().ServiceSetCharm(
  2158  		"service", "cs:precise/wordpress-3", true,
  2159  	)
  2160  	c.Assert(err, jc.ErrorIsNil)
  2161  
  2162  	// Ensure that the charm is marked as forced.
  2163  	service, err := s.State.Service("service")
  2164  	c.Assert(err, jc.ErrorIsNil)
  2165  	charm, force, err := service.Charm()
  2166  	c.Assert(err, jc.ErrorIsNil)
  2167  	c.Assert(charm.URL().String(), gc.Equals, "cs:precise/wordpress-3")
  2168  	c.Assert(force, jc.IsTrue)
  2169  }
  2170  
  2171  func (s *clientRepoSuite) TestBlockServiceSetCharmForce(c *gc.C) {
  2172  	s.setupServiceSetCharm(c)
  2173  
  2174  	// block all changes
  2175  	s.BlockAllChanges(c, "TestBlockServiceSetCharmForce")
  2176  	s.BlockRemoveObject(c, "TestBlockServiceSetCharmForce")
  2177  	s.BlockDestroyEnvironment(c, "TestBlockServiceSetCharmForce")
  2178  
  2179  	s.assertServiceSetCharm(c, true)
  2180  }
  2181  
  2182  func (s *clientSuite) TestClientServiceSetCharmInvalidService(c *gc.C) {
  2183  	err := s.APIState.Client().ServiceSetCharm(
  2184  		"badservice", "cs:precise/wordpress-3", true,
  2185  	)
  2186  	c.Assert(err, gc.ErrorMatches, `service "badservice" not found`)
  2187  }
  2188  
  2189  func (s *clientRepoSuite) TestClientServiceSetCharmErrors(c *gc.C) {
  2190  	s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  2191  	for url, expect := range map[string]string{
  2192  		// TODO(fwereade,Makyo) make these errors consistent one day.
  2193  		"wordpress":                   "charm url series is not resolved",
  2194  		"cs:wordpress":                "charm url series is not resolved",
  2195  		"cs:precise/wordpress":        "charm url must include revision",
  2196  		"cs:precise/wordpress-999999": `cannot retrieve charm "cs:precise/wordpress-999999": charm not found`,
  2197  	} {
  2198  		c.Logf("test %s", url)
  2199  		err := s.APIState.Client().ServiceSetCharm(
  2200  			"wordpress", url, false,
  2201  		)
  2202  		c.Check(err, gc.ErrorMatches, expect)
  2203  	}
  2204  }
  2205  
  2206  func (s *clientSuite) checkEndpoints(c *gc.C, endpoints map[string]charm.Relation) {
  2207  	c.Assert(endpoints["wordpress"], gc.DeepEquals, charm.Relation{
  2208  		Name:      "db",
  2209  		Role:      charm.RelationRole("requirer"),
  2210  		Interface: "mysql",
  2211  		Optional:  false,
  2212  		Limit:     1,
  2213  		Scope:     charm.RelationScope("global"),
  2214  	})
  2215  	c.Assert(endpoints["mysql"], gc.DeepEquals, charm.Relation{
  2216  		Name:      "server",
  2217  		Role:      charm.RelationRole("provider"),
  2218  		Interface: "mysql",
  2219  		Optional:  false,
  2220  		Limit:     0,
  2221  		Scope:     charm.RelationScope("global"),
  2222  	})
  2223  }
  2224  
  2225  func (s *clientSuite) assertAddRelation(c *gc.C, endpoints []string) {
  2226  	s.setUpScenario(c)
  2227  	res, err := s.APIState.Client().AddRelation(endpoints...)
  2228  	c.Assert(err, jc.ErrorIsNil)
  2229  	s.checkEndpoints(c, res.Endpoints)
  2230  	// Show that the relation was added.
  2231  	wpSvc, err := s.State.Service("wordpress")
  2232  	c.Assert(err, jc.ErrorIsNil)
  2233  	rels, err := wpSvc.Relations()
  2234  	// There are 2 relations - the logging-wordpress one set up in the
  2235  	// scenario and the one created in this test.
  2236  	c.Assert(len(rels), gc.Equals, 2)
  2237  	mySvc, err := s.State.Service("mysql")
  2238  	c.Assert(err, jc.ErrorIsNil)
  2239  	rels, err = mySvc.Relations()
  2240  	c.Assert(err, jc.ErrorIsNil)
  2241  	c.Assert(len(rels), gc.Equals, 1)
  2242  }
  2243  
  2244  func (s *clientSuite) TestSuccessfullyAddRelation(c *gc.C) {
  2245  	endpoints := []string{"wordpress", "mysql"}
  2246  	s.assertAddRelation(c, endpoints)
  2247  }
  2248  
  2249  func (s *clientSuite) TestBlockDestroyAddRelation(c *gc.C) {
  2250  	s.BlockDestroyEnvironment(c, "TestBlockDestroyAddRelation")
  2251  	s.assertAddRelation(c, []string{"wordpress", "mysql"})
  2252  }
  2253  func (s *clientSuite) TestBlockRemoveAddRelation(c *gc.C) {
  2254  	s.BlockRemoveObject(c, "TestBlockRemoveAddRelation")
  2255  	s.assertAddRelation(c, []string{"wordpress", "mysql"})
  2256  }
  2257  
  2258  func (s *clientSuite) TestBlockChangesAddRelation(c *gc.C) {
  2259  	s.setUpScenario(c)
  2260  	s.BlockAllChanges(c, "TestBlockChangesAddRelation")
  2261  	_, err := s.APIState.Client().AddRelation([]string{"wordpress", "mysql"}...)
  2262  	s.AssertBlocked(c, err, "TestBlockChangesAddRelation")
  2263  }
  2264  
  2265  func (s *clientSuite) TestSuccessfullyAddRelationSwapped(c *gc.C) {
  2266  	// Show that the order of the services listed in the AddRelation call
  2267  	// does not matter.  This is a repeat of the previous test with the service
  2268  	// names swapped.
  2269  	endpoints := []string{"mysql", "wordpress"}
  2270  	s.assertAddRelation(c, endpoints)
  2271  }
  2272  
  2273  func (s *clientSuite) TestCallWithOnlyOneEndpoint(c *gc.C) {
  2274  	s.setUpScenario(c)
  2275  	endpoints := []string{"wordpress"}
  2276  	_, err := s.APIState.Client().AddRelation(endpoints...)
  2277  	c.Assert(err, gc.ErrorMatches, "no relations found")
  2278  }
  2279  
  2280  func (s *clientSuite) TestCallWithOneEndpointTooMany(c *gc.C) {
  2281  	s.setUpScenario(c)
  2282  	endpoints := []string{"wordpress", "mysql", "logging"}
  2283  	_, err := s.APIState.Client().AddRelation(endpoints...)
  2284  	c.Assert(err, gc.ErrorMatches, "cannot relate 3 endpoints")
  2285  }
  2286  
  2287  func (s *clientSuite) TestAddAlreadyAddedRelation(c *gc.C) {
  2288  	s.setUpScenario(c)
  2289  	// Add a relation between wordpress and mysql.
  2290  	endpoints := []string{"wordpress", "mysql"}
  2291  	eps, err := s.State.InferEndpoints(endpoints...)
  2292  	c.Assert(err, jc.ErrorIsNil)
  2293  	_, err = s.State.AddRelation(eps...)
  2294  	c.Assert(err, jc.ErrorIsNil)
  2295  	// And try to add it again.
  2296  	_, err = s.APIState.Client().AddRelation(endpoints...)
  2297  	c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": relation already exists`)
  2298  }
  2299  
  2300  func (s *clientSuite) setupRelationScenario(c *gc.C, endpoints []string) *state.Relation {
  2301  	s.setUpScenario(c)
  2302  	// Add a relation between the endpoints.
  2303  	eps, err := s.State.InferEndpoints(endpoints...)
  2304  	c.Assert(err, jc.ErrorIsNil)
  2305  	relation, err := s.State.AddRelation(eps...)
  2306  	c.Assert(err, jc.ErrorIsNil)
  2307  	return relation
  2308  }
  2309  
  2310  func (s *clientSuite) assertDestroyRelation(c *gc.C, endpoints []string) {
  2311  	s.assertDestroyRelationSuccess(
  2312  		c,
  2313  		s.setupRelationScenario(c, endpoints),
  2314  		endpoints)
  2315  }
  2316  
  2317  func (s *clientSuite) assertDestroyRelationSuccess(c *gc.C, relation *state.Relation, endpoints []string) {
  2318  	err := s.APIState.Client().DestroyRelation(endpoints...)
  2319  	c.Assert(err, jc.ErrorIsNil)
  2320  	// Show that the relation was removed.
  2321  	c.Assert(relation.Refresh(), jc.Satisfies, errors.IsNotFound)
  2322  }
  2323  
  2324  func (s *clientSuite) TestSuccessfulDestroyRelation(c *gc.C) {
  2325  	endpoints := []string{"wordpress", "mysql"}
  2326  	s.assertDestroyRelation(c, endpoints)
  2327  }
  2328  
  2329  func (s *clientSuite) TestSuccessfullyDestroyRelationSwapped(c *gc.C) {
  2330  	// Show that the order of the services listed in the DestroyRelation call
  2331  	// does not matter.  This is a repeat of the previous test with the service
  2332  	// names swapped.
  2333  	endpoints := []string{"mysql", "wordpress"}
  2334  	s.assertDestroyRelation(c, endpoints)
  2335  }
  2336  
  2337  func (s *clientSuite) TestNoRelation(c *gc.C) {
  2338  	s.setUpScenario(c)
  2339  	endpoints := []string{"wordpress", "mysql"}
  2340  	err := s.APIState.Client().DestroyRelation(endpoints...)
  2341  	c.Assert(err, gc.ErrorMatches, `relation "wordpress:db mysql:server" not found`)
  2342  }
  2343  
  2344  func (s *clientSuite) TestAttemptDestroyingNonExistentRelation(c *gc.C) {
  2345  	s.setUpScenario(c)
  2346  	s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak"))
  2347  	endpoints := []string{"riak", "wordpress"}
  2348  	err := s.APIState.Client().DestroyRelation(endpoints...)
  2349  	c.Assert(err, gc.ErrorMatches, "no relations found")
  2350  }
  2351  
  2352  func (s *clientSuite) TestAttemptDestroyingWithOnlyOneEndpoint(c *gc.C) {
  2353  	s.setUpScenario(c)
  2354  	endpoints := []string{"wordpress"}
  2355  	err := s.APIState.Client().DestroyRelation(endpoints...)
  2356  	c.Assert(err, gc.ErrorMatches, "no relations found")
  2357  }
  2358  
  2359  func (s *clientSuite) TestAttemptDestroyingPeerRelation(c *gc.C) {
  2360  	s.setUpScenario(c)
  2361  	s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak"))
  2362  
  2363  	endpoints := []string{"riak:ring"}
  2364  	err := s.APIState.Client().DestroyRelation(endpoints...)
  2365  	c.Assert(err, gc.ErrorMatches, `cannot destroy relation "riak:ring": is a peer relation`)
  2366  }
  2367  
  2368  func (s *clientSuite) TestAttemptDestroyingAlreadyDestroyedRelation(c *gc.C) {
  2369  	s.setUpScenario(c)
  2370  
  2371  	// Add a relation between wordpress and mysql.
  2372  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  2373  	c.Assert(err, jc.ErrorIsNil)
  2374  	rel, err := s.State.AddRelation(eps...)
  2375  	c.Assert(err, jc.ErrorIsNil)
  2376  
  2377  	endpoints := []string{"wordpress", "mysql"}
  2378  	err = s.APIState.Client().DestroyRelation(endpoints...)
  2379  	// Show that the relation was removed.
  2380  	c.Assert(rel.Refresh(), jc.Satisfies, errors.IsNotFound)
  2381  
  2382  	// And try to destroy it again.
  2383  	err = s.APIState.Client().DestroyRelation(endpoints...)
  2384  	c.Assert(err, gc.ErrorMatches, `relation "wordpress:db mysql:server" not found`)
  2385  }
  2386  
  2387  func (s *clientSuite) TestClientWatchAll(c *gc.C) {
  2388  	// A very simple end-to-end test, because
  2389  	// all the logic is tested elsewhere.
  2390  	m, err := s.State.AddMachine("quantal", state.JobManageEnviron)
  2391  	c.Assert(err, jc.ErrorIsNil)
  2392  	err = m.SetProvisioned("i-0", agent.BootstrapNonce, nil)
  2393  	c.Assert(err, jc.ErrorIsNil)
  2394  	watcher, err := s.APIState.Client().WatchAll()
  2395  	c.Assert(err, jc.ErrorIsNil)
  2396  	defer func() {
  2397  		err := watcher.Stop()
  2398  		c.Assert(err, jc.ErrorIsNil)
  2399  	}()
  2400  	deltas, err := watcher.Next()
  2401  	c.Assert(err, jc.ErrorIsNil)
  2402  	if !c.Check(deltas, gc.DeepEquals, []multiwatcher.Delta{{
  2403  		Entity: &multiwatcher.MachineInfo{
  2404  			Id:                      m.Id(),
  2405  			InstanceId:              "i-0",
  2406  			Status:                  multiwatcher.Status("pending"),
  2407  			Life:                    multiwatcher.Life("alive"),
  2408  			Series:                  "quantal",
  2409  			Jobs:                    []multiwatcher.MachineJob{state.JobManageEnviron.ToParams()},
  2410  			Addresses:               []network.Address{},
  2411  			HardwareCharacteristics: &instance.HardwareCharacteristics{},
  2412  			HasVote:                 false,
  2413  			WantsVote:               true,
  2414  		},
  2415  	}}) {
  2416  		c.Logf("got:")
  2417  		for _, d := range deltas {
  2418  			c.Logf("%#v\n", d.Entity)
  2419  		}
  2420  	}
  2421  }
  2422  
  2423  func (s *clientSuite) TestClientSetServiceConstraints(c *gc.C) {
  2424  	service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
  2425  
  2426  	// Update constraints for the service.
  2427  	cons, err := constraints.Parse("mem=4096", "cpu-cores=2")
  2428  	c.Assert(err, jc.ErrorIsNil)
  2429  	err = s.APIState.Client().SetServiceConstraints("dummy", cons)
  2430  	c.Assert(err, jc.ErrorIsNil)
  2431  
  2432  	// Ensure the constraints have been correctly updated.
  2433  	obtained, err := service.Constraints()
  2434  	c.Assert(err, jc.ErrorIsNil)
  2435  	c.Assert(obtained, gc.DeepEquals, cons)
  2436  }
  2437  
  2438  func (s *clientSuite) setupSetServiceConstraints(c *gc.C) (*state.Service, constraints.Value) {
  2439  	service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
  2440  	// Update constraints for the service.
  2441  	cons, err := constraints.Parse("mem=4096", "cpu-cores=2")
  2442  	c.Assert(err, jc.ErrorIsNil)
  2443  	return service, cons
  2444  }
  2445  
  2446  func (s *clientSuite) assertSetServiceConstraints(c *gc.C, service *state.Service, cons constraints.Value) {
  2447  	err := s.APIState.Client().SetServiceConstraints("dummy", cons)
  2448  	c.Assert(err, jc.ErrorIsNil)
  2449  	// Ensure the constraints have been correctly updated.
  2450  	obtained, err := service.Constraints()
  2451  	c.Assert(err, jc.ErrorIsNil)
  2452  	c.Assert(obtained, gc.DeepEquals, cons)
  2453  }
  2454  
  2455  func (s *clientSuite) assertSetServiceConstraintsBlocked(c *gc.C, msg string, service *state.Service, cons constraints.Value) {
  2456  	err := s.APIState.Client().SetServiceConstraints("dummy", cons)
  2457  	s.AssertBlocked(c, err, msg)
  2458  }
  2459  
  2460  func (s *clientSuite) TestBlockDestroySetServiceConstraints(c *gc.C) {
  2461  	svc, cons := s.setupSetServiceConstraints(c)
  2462  	s.BlockDestroyEnvironment(c, "TestBlockDestroySetServiceConstraints")
  2463  	s.assertSetServiceConstraints(c, svc, cons)
  2464  }
  2465  
  2466  func (s *clientSuite) TestBlockRemoveSetServiceConstraints(c *gc.C) {
  2467  	svc, cons := s.setupSetServiceConstraints(c)
  2468  	s.BlockRemoveObject(c, "TestBlockRemoveSetServiceConstraints")
  2469  	s.assertSetServiceConstraints(c, svc, cons)
  2470  }
  2471  
  2472  func (s *clientSuite) TestBlockChangesSetServiceConstraints(c *gc.C) {
  2473  	svc, cons := s.setupSetServiceConstraints(c)
  2474  	s.BlockAllChanges(c, "TestBlockChangesSetServiceConstraints")
  2475  	s.assertSetServiceConstraintsBlocked(c, "TestBlockChangesSetServiceConstraints", svc, cons)
  2476  }
  2477  
  2478  func (s *clientSuite) TestClientGetServiceConstraints(c *gc.C) {
  2479  	service := s.AddTestingService(c, "dummy", s.AddTestingCharm(c, "dummy"))
  2480  
  2481  	// Set constraints for the service.
  2482  	cons, err := constraints.Parse("mem=4096", "cpu-cores=2")
  2483  	c.Assert(err, jc.ErrorIsNil)
  2484  	err = service.SetConstraints(cons)
  2485  	c.Assert(err, jc.ErrorIsNil)
  2486  
  2487  	// Check we can get the constraints.
  2488  	obtained, err := s.APIState.Client().GetServiceConstraints("dummy")
  2489  	c.Assert(err, jc.ErrorIsNil)
  2490  	c.Assert(obtained, gc.DeepEquals, cons)
  2491  }
  2492  
  2493  func (s *clientSuite) TestClientSetEnvironmentConstraints(c *gc.C) {
  2494  	// Set constraints for the environment.
  2495  	cons, err := constraints.Parse("mem=4096", "cpu-cores=2")
  2496  	c.Assert(err, jc.ErrorIsNil)
  2497  	err = s.APIState.Client().SetEnvironmentConstraints(cons)
  2498  	c.Assert(err, jc.ErrorIsNil)
  2499  
  2500  	// Ensure the constraints have been correctly updated.
  2501  	obtained, err := s.State.EnvironConstraints()
  2502  	c.Assert(err, jc.ErrorIsNil)
  2503  	c.Assert(obtained, gc.DeepEquals, cons)
  2504  }
  2505  
  2506  func (s *clientSuite) assertSetEnvironmentConstraints(c *gc.C) {
  2507  	// Set constraints for the environment.
  2508  	cons, err := constraints.Parse("mem=4096", "cpu-cores=2")
  2509  	c.Assert(err, jc.ErrorIsNil)
  2510  	err = s.APIState.Client().SetEnvironmentConstraints(cons)
  2511  	c.Assert(err, jc.ErrorIsNil)
  2512  	// Ensure the constraints have been correctly updated.
  2513  	obtained, err := s.State.EnvironConstraints()
  2514  	c.Assert(err, jc.ErrorIsNil)
  2515  	c.Assert(obtained, gc.DeepEquals, cons)
  2516  }
  2517  
  2518  func (s *clientSuite) assertSetEnvironmentConstraintsBlocked(c *gc.C, msg string) {
  2519  	// Set constraints for the environment.
  2520  	cons, err := constraints.Parse("mem=4096", "cpu-cores=2")
  2521  	c.Assert(err, jc.ErrorIsNil)
  2522  	err = s.APIState.Client().SetEnvironmentConstraints(cons)
  2523  	s.AssertBlocked(c, err, msg)
  2524  }
  2525  
  2526  func (s *clientSuite) TestBlockDestroyClientSetEnvironmentConstraints(c *gc.C) {
  2527  	s.BlockDestroyEnvironment(c, "TestBlockDestroyClientSetEnvironmentConstraints")
  2528  	s.assertSetEnvironmentConstraints(c)
  2529  }
  2530  
  2531  func (s *clientSuite) TestBlockRemoveClientSetEnvironmentConstraints(c *gc.C) {
  2532  	s.BlockRemoveObject(c, "TestBlockRemoveClientSetEnvironmentConstraints")
  2533  	s.assertSetEnvironmentConstraints(c)
  2534  }
  2535  
  2536  func (s *clientSuite) TestBlockChangesClientSetEnvironmentConstraints(c *gc.C) {
  2537  	s.BlockAllChanges(c, "TestBlockChangesClientSetEnvironmentConstraints")
  2538  	s.assertSetEnvironmentConstraintsBlocked(c, "TestBlockChangesClientSetEnvironmentConstraints")
  2539  }
  2540  
  2541  func (s *clientSuite) TestClientGetEnvironmentConstraints(c *gc.C) {
  2542  	// Set constraints for the environment.
  2543  	cons, err := constraints.Parse("mem=4096", "cpu-cores=2")
  2544  	c.Assert(err, jc.ErrorIsNil)
  2545  	err = s.State.SetEnvironConstraints(cons)
  2546  	c.Assert(err, jc.ErrorIsNil)
  2547  
  2548  	// Check we can get the constraints.
  2549  	obtained, err := s.APIState.Client().GetEnvironmentConstraints()
  2550  	c.Assert(err, jc.ErrorIsNil)
  2551  	c.Assert(obtained, gc.DeepEquals, cons)
  2552  }
  2553  
  2554  func (s *clientSuite) TestClientServiceCharmRelations(c *gc.C) {
  2555  	s.setUpScenario(c)
  2556  	_, err := s.APIState.Client().ServiceCharmRelations("blah")
  2557  	c.Assert(err, gc.ErrorMatches, `service "blah" not found`)
  2558  
  2559  	relations, err := s.APIState.Client().ServiceCharmRelations("wordpress")
  2560  	c.Assert(err, jc.ErrorIsNil)
  2561  	c.Assert(relations, gc.DeepEquals, []string{
  2562  		"cache", "db", "juju-info", "logging-dir", "monitoring-port", "url",
  2563  	})
  2564  }
  2565  
  2566  func (s *clientSuite) TestClientPublicAddressErrors(c *gc.C) {
  2567  	s.setUpScenario(c)
  2568  	_, err := s.APIState.Client().PublicAddress("wordpress")
  2569  	c.Assert(err, gc.ErrorMatches, `unknown unit or machine "wordpress"`)
  2570  	_, err = s.APIState.Client().PublicAddress("0")
  2571  	c.Assert(err, gc.ErrorMatches, `machine "0" has no public address`)
  2572  	_, err = s.APIState.Client().PublicAddress("wordpress/0")
  2573  	c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" has no public address`)
  2574  }
  2575  
  2576  func (s *clientSuite) TestClientPublicAddressMachine(c *gc.C) {
  2577  	s.setUpScenario(c)
  2578  
  2579  	// Internally, network.SelectPublicAddress is used; the "most public"
  2580  	// address is returned.
  2581  	m1, err := s.State.Machine("1")
  2582  	c.Assert(err, jc.ErrorIsNil)
  2583  	cloudLocalAddress := network.NewScopedAddress("cloudlocal", network.ScopeCloudLocal)
  2584  	publicAddress := network.NewScopedAddress("public", network.ScopePublic)
  2585  	err = m1.SetProviderAddresses(cloudLocalAddress)
  2586  	c.Assert(err, jc.ErrorIsNil)
  2587  	addr, err := s.APIState.Client().PublicAddress("1")
  2588  	c.Assert(err, jc.ErrorIsNil)
  2589  	c.Assert(addr, gc.Equals, "cloudlocal")
  2590  	err = m1.SetProviderAddresses(cloudLocalAddress, publicAddress)
  2591  	addr, err = s.APIState.Client().PublicAddress("1")
  2592  	c.Assert(err, jc.ErrorIsNil)
  2593  	c.Assert(addr, gc.Equals, "public")
  2594  }
  2595  
  2596  func (s *clientSuite) TestClientPublicAddressUnit(c *gc.C) {
  2597  	s.setUpScenario(c)
  2598  
  2599  	m1, err := s.State.Machine("1")
  2600  	publicAddress := network.NewScopedAddress("public", network.ScopePublic)
  2601  	err = m1.SetProviderAddresses(publicAddress)
  2602  	c.Assert(err, jc.ErrorIsNil)
  2603  	addr, err := s.APIState.Client().PublicAddress("wordpress/0")
  2604  	c.Assert(err, jc.ErrorIsNil)
  2605  	c.Assert(addr, gc.Equals, "public")
  2606  }
  2607  
  2608  func (s *clientSuite) TestClientPrivateAddressErrors(c *gc.C) {
  2609  	s.setUpScenario(c)
  2610  	_, err := s.APIState.Client().PrivateAddress("wordpress")
  2611  	c.Assert(err, gc.ErrorMatches, `unknown unit or machine "wordpress"`)
  2612  	_, err = s.APIState.Client().PrivateAddress("0")
  2613  	c.Assert(err, gc.ErrorMatches, `machine "0" has no internal address`)
  2614  	_, err = s.APIState.Client().PrivateAddress("wordpress/0")
  2615  	c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" has no internal address`)
  2616  }
  2617  
  2618  func (s *clientSuite) TestClientPrivateAddress(c *gc.C) {
  2619  	s.setUpScenario(c)
  2620  
  2621  	// Internally, network.SelectInternalAddress is used; the public
  2622  	// address if no cloud-local one is available.
  2623  	m1, err := s.State.Machine("1")
  2624  	c.Assert(err, jc.ErrorIsNil)
  2625  	cloudLocalAddress := network.NewScopedAddress("cloudlocal", network.ScopeCloudLocal)
  2626  	publicAddress := network.NewScopedAddress("public", network.ScopePublic)
  2627  	err = m1.SetProviderAddresses(publicAddress)
  2628  	c.Assert(err, jc.ErrorIsNil)
  2629  	addr, err := s.APIState.Client().PrivateAddress("1")
  2630  	c.Assert(err, jc.ErrorIsNil)
  2631  	c.Assert(addr, gc.Equals, "public")
  2632  	err = m1.SetProviderAddresses(cloudLocalAddress, publicAddress)
  2633  	addr, err = s.APIState.Client().PrivateAddress("1")
  2634  	c.Assert(err, jc.ErrorIsNil)
  2635  	c.Assert(addr, gc.Equals, "cloudlocal")
  2636  }
  2637  
  2638  func (s *clientSuite) TestClientPrivateAddressUnit(c *gc.C) {
  2639  	s.setUpScenario(c)
  2640  
  2641  	m1, err := s.State.Machine("1")
  2642  	privateAddress := network.NewScopedAddress("private", network.ScopeCloudLocal)
  2643  	err = m1.SetProviderAddresses(privateAddress)
  2644  	c.Assert(err, jc.ErrorIsNil)
  2645  	addr, err := s.APIState.Client().PrivateAddress("wordpress/0")
  2646  	c.Assert(err, jc.ErrorIsNil)
  2647  	c.Assert(addr, gc.Equals, "private")
  2648  }
  2649  
  2650  func (s *serverSuite) TestClientEnvironmentGet(c *gc.C) {
  2651  	envConfig, err := s.State.EnvironConfig()
  2652  	c.Assert(err, jc.ErrorIsNil)
  2653  	result, err := s.client.EnvironmentGet()
  2654  	c.Assert(err, jc.ErrorIsNil)
  2655  	c.Assert(result.Config, gc.DeepEquals, envConfig.AllAttrs())
  2656  }
  2657  
  2658  func (s *serverSuite) assertEnvValue(c *gc.C, key string, expected interface{}) {
  2659  	envConfig, err := s.State.EnvironConfig()
  2660  	c.Assert(err, jc.ErrorIsNil)
  2661  	value, found := envConfig.AllAttrs()[key]
  2662  	c.Assert(found, jc.IsTrue)
  2663  	c.Assert(value, gc.Equals, expected)
  2664  }
  2665  
  2666  func (s *serverSuite) assertEnvValueMissing(c *gc.C, key string) {
  2667  	envConfig, err := s.State.EnvironConfig()
  2668  	c.Assert(err, jc.ErrorIsNil)
  2669  	_, found := envConfig.AllAttrs()[key]
  2670  	c.Assert(found, jc.IsFalse)
  2671  }
  2672  
  2673  func (s *serverSuite) TestClientEnvironmentSet(c *gc.C) {
  2674  	envConfig, err := s.State.EnvironConfig()
  2675  	c.Assert(err, jc.ErrorIsNil)
  2676  	_, found := envConfig.AllAttrs()["some-key"]
  2677  	c.Assert(found, jc.IsFalse)
  2678  
  2679  	params := params.EnvironmentSet{
  2680  		Config: map[string]interface{}{
  2681  			"some-key":  "value",
  2682  			"other-key": "other value"},
  2683  	}
  2684  	err = s.client.EnvironmentSet(params)
  2685  	c.Assert(err, jc.ErrorIsNil)
  2686  	s.assertEnvValue(c, "some-key", "value")
  2687  	s.assertEnvValue(c, "other-key", "other value")
  2688  }
  2689  
  2690  func (s *serverSuite) TestClientEnvironmentSetImmutable(c *gc.C) {
  2691  	// The various immutable config values are tested in
  2692  	// environs/config/config_test.go, so just choosing one here.
  2693  	params := params.EnvironmentSet{
  2694  		Config: map[string]interface{}{"state-port": "1"},
  2695  	}
  2696  	err := s.client.EnvironmentSet(params)
  2697  	c.Check(err, gc.ErrorMatches, `cannot change state-port from .* to 1`)
  2698  }
  2699  
  2700  func (s *serverSuite) assertEnvironmentSetBlocked(c *gc.C, args map[string]interface{}, msg string) {
  2701  	err := s.client.EnvironmentSet(params.EnvironmentSet{args})
  2702  	s.AssertBlocked(c, err, msg)
  2703  }
  2704  
  2705  func (s *serverSuite) TestBlockChangesClientEnvironmentSet(c *gc.C) {
  2706  	s.BlockAllChanges(c, "TestBlockChangesClientEnvironmentSet")
  2707  	args := map[string]interface{}{"some-key": "value"}
  2708  	s.assertEnvironmentSetBlocked(c, args, "TestBlockChangesClientEnvironmentSet")
  2709  }
  2710  
  2711  func (s *serverSuite) TestClientEnvironmentSetDeprecated(c *gc.C) {
  2712  	envConfig, err := s.State.EnvironConfig()
  2713  	c.Assert(err, jc.ErrorIsNil)
  2714  	url := envConfig.AllAttrs()["agent-metadata-url"]
  2715  	c.Assert(url, gc.Equals, "")
  2716  
  2717  	args := params.EnvironmentSet{
  2718  		Config: map[string]interface{}{"tools-metadata-url": "value"},
  2719  	}
  2720  	err = s.client.EnvironmentSet(args)
  2721  	c.Assert(err, jc.ErrorIsNil)
  2722  	s.assertEnvValue(c, "agent-metadata-url", "value")
  2723  	s.assertEnvValue(c, "tools-metadata-url", "value")
  2724  }
  2725  
  2726  func (s *serverSuite) TestClientEnvironmentSetCannotChangeAgentVersion(c *gc.C) {
  2727  	args := params.EnvironmentSet{
  2728  		map[string]interface{}{"agent-version": "9.9.9"},
  2729  	}
  2730  	err := s.client.EnvironmentSet(args)
  2731  	c.Assert(err, gc.ErrorMatches, "agent-version cannot be changed")
  2732  
  2733  	// It's okay to pass env back with the same agent-version.
  2734  	result, err := s.client.EnvironmentGet()
  2735  	c.Assert(err, jc.ErrorIsNil)
  2736  	c.Assert(result.Config["agent-version"], gc.NotNil)
  2737  	args.Config["agent-version"] = result.Config["agent-version"]
  2738  	err = s.client.EnvironmentSet(args)
  2739  	c.Assert(err, jc.ErrorIsNil)
  2740  }
  2741  
  2742  func (s *serverSuite) TestClientEnvironmentUnset(c *gc.C) {
  2743  	err := s.State.UpdateEnvironConfig(map[string]interface{}{"abc": 123}, nil, nil)
  2744  	c.Assert(err, jc.ErrorIsNil)
  2745  
  2746  	args := params.EnvironmentUnset{[]string{"abc"}}
  2747  	err = s.client.EnvironmentUnset(args)
  2748  	c.Assert(err, jc.ErrorIsNil)
  2749  	s.assertEnvValueMissing(c, "abc")
  2750  }
  2751  
  2752  func (s *serverSuite) TestBlockClientEnvironmentUnset(c *gc.C) {
  2753  	err := s.State.UpdateEnvironConfig(map[string]interface{}{"abc": 123}, nil, nil)
  2754  	c.Assert(err, jc.ErrorIsNil)
  2755  	s.BlockAllChanges(c, "TestBlockClientEnvironmentUnset")
  2756  
  2757  	args := params.EnvironmentUnset{[]string{"abc"}}
  2758  	err = s.client.EnvironmentUnset(args)
  2759  	s.AssertBlocked(c, err, "TestBlockClientEnvironmentUnset")
  2760  }
  2761  
  2762  func (s *serverSuite) TestClientEnvironmentUnsetMissing(c *gc.C) {
  2763  	// It's okay to unset a non-existent attribute.
  2764  	args := params.EnvironmentUnset{[]string{"not_there"}}
  2765  	err := s.client.EnvironmentUnset(args)
  2766  	c.Assert(err, jc.ErrorIsNil)
  2767  }
  2768  
  2769  func (s *serverSuite) TestClientEnvironmentUnsetError(c *gc.C) {
  2770  	err := s.State.UpdateEnvironConfig(map[string]interface{}{"abc": 123}, nil, nil)
  2771  	c.Assert(err, jc.ErrorIsNil)
  2772  
  2773  	// "type" may not be removed, and this will cause an error.
  2774  	// If any one attribute's removal causes an error, there
  2775  	// should be no change.
  2776  	args := params.EnvironmentUnset{[]string{"abc", "type"}}
  2777  	err = s.client.EnvironmentUnset(args)
  2778  	c.Assert(err, gc.ErrorMatches, "type: expected string, got nothing")
  2779  	s.assertEnvValue(c, "abc", 123)
  2780  }
  2781  
  2782  func (s *clientSuite) TestClientFindTools(c *gc.C) {
  2783  	result, err := s.APIState.Client().FindTools(2, -1, "", "")
  2784  	c.Assert(err, jc.ErrorIsNil)
  2785  	c.Assert(result.Error, jc.Satisfies, params.IsCodeNotFound)
  2786  	toolstesting.UploadToStorage(c, s.DefaultToolsStorage, "released", version.MustParseBinary("2.12.0-precise-amd64"))
  2787  	result, err = s.APIState.Client().FindTools(2, 12, "precise", "amd64")
  2788  	c.Assert(err, jc.ErrorIsNil)
  2789  	c.Assert(result.Error, gc.IsNil)
  2790  	c.Assert(result.List, gc.HasLen, 1)
  2791  	c.Assert(result.List[0].Version, gc.Equals, version.MustParseBinary("2.12.0-precise-amd64"))
  2792  	url := fmt.Sprintf("https://%s/environment/%s/tools/%s",
  2793  		s.APIState.Addr(), coretesting.EnvironmentTag.Id(), result.List[0].Version)
  2794  	c.Assert(result.List[0].URL, gc.Equals, url)
  2795  }
  2796  
  2797  func (s *clientSuite) checkMachine(c *gc.C, id, series, cons string) {
  2798  	// Ensure the machine was actually created.
  2799  	machine, err := s.BackingState.Machine(id)
  2800  	c.Assert(err, jc.ErrorIsNil)
  2801  	c.Assert(machine.Series(), gc.Equals, series)
  2802  	c.Assert(machine.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobHostUnits})
  2803  	machineConstraints, err := machine.Constraints()
  2804  	c.Assert(err, jc.ErrorIsNil)
  2805  	c.Assert(machineConstraints.String(), gc.Equals, cons)
  2806  }
  2807  
  2808  func (s *clientSuite) TestClientAddMachinesDefaultSeries(c *gc.C) {
  2809  	apiParams := make([]params.AddMachineParams, 3)
  2810  	for i := 0; i < 3; i++ {
  2811  		apiParams[i] = params.AddMachineParams{
  2812  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2813  		}
  2814  	}
  2815  	machines, err := s.APIState.Client().AddMachines(apiParams)
  2816  	c.Assert(err, jc.ErrorIsNil)
  2817  	c.Assert(len(machines), gc.Equals, 3)
  2818  	for i, machineResult := range machines {
  2819  		c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
  2820  		s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String())
  2821  	}
  2822  }
  2823  
  2824  func (s *clientSuite) assertAddMachines(c *gc.C) {
  2825  	apiParams := make([]params.AddMachineParams, 3)
  2826  	for i := 0; i < 3; i++ {
  2827  		apiParams[i] = params.AddMachineParams{
  2828  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2829  		}
  2830  	}
  2831  	machines, err := s.APIState.Client().AddMachines(apiParams)
  2832  	c.Assert(err, jc.ErrorIsNil)
  2833  	c.Assert(len(machines), gc.Equals, 3)
  2834  	for i, machineResult := range machines {
  2835  		c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
  2836  		s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String())
  2837  	}
  2838  }
  2839  
  2840  func (s *clientSuite) assertAddMachinesBlocked(c *gc.C, msg string) {
  2841  	apiParams := make([]params.AddMachineParams, 3)
  2842  	for i := 0; i < 3; i++ {
  2843  		apiParams[i] = params.AddMachineParams{
  2844  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2845  		}
  2846  	}
  2847  	_, err := s.APIState.Client().AddMachines(apiParams)
  2848  	s.AssertBlocked(c, err, msg)
  2849  }
  2850  
  2851  func (s *clientSuite) TestBlockDestroyClientAddMachinesDefaultSeries(c *gc.C) {
  2852  	s.BlockDestroyEnvironment(c, "TestBlockDestroyClientAddMachinesDefaultSeries")
  2853  	s.assertAddMachines(c)
  2854  }
  2855  
  2856  func (s *clientSuite) TestBlockRemoveClientAddMachinesDefaultSeries(c *gc.C) {
  2857  	s.BlockRemoveObject(c, "TestBlockRemoveClientAddMachinesDefaultSeries")
  2858  	s.assertAddMachines(c)
  2859  }
  2860  
  2861  func (s *clientSuite) TestBlockChangesClientAddMachines(c *gc.C) {
  2862  	s.BlockAllChanges(c, "TestBlockChangesClientAddMachines")
  2863  	s.assertAddMachinesBlocked(c, "TestBlockChangesClientAddMachines")
  2864  }
  2865  
  2866  func (s *clientSuite) TestClientAddMachinesWithSeries(c *gc.C) {
  2867  	apiParams := make([]params.AddMachineParams, 3)
  2868  	for i := 0; i < 3; i++ {
  2869  		apiParams[i] = params.AddMachineParams{
  2870  			Series: "quantal",
  2871  			Jobs:   []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2872  		}
  2873  	}
  2874  	machines, err := s.APIState.Client().AddMachines(apiParams)
  2875  	c.Assert(err, jc.ErrorIsNil)
  2876  	c.Assert(len(machines), gc.Equals, 3)
  2877  	for i, machineResult := range machines {
  2878  		c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
  2879  		s.checkMachine(c, machineResult.Machine, "quantal", apiParams[i].Constraints.String())
  2880  	}
  2881  }
  2882  
  2883  func (s *clientSuite) TestClientAddMachineInsideMachine(c *gc.C) {
  2884  	_, err := s.State.AddMachine("quantal", state.JobHostUnits)
  2885  	c.Assert(err, jc.ErrorIsNil)
  2886  
  2887  	machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{{
  2888  		Jobs:          []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2889  		ContainerType: instance.LXC,
  2890  		ParentId:      "0",
  2891  		Series:        "quantal",
  2892  	}})
  2893  	c.Assert(err, jc.ErrorIsNil)
  2894  	c.Assert(machines, gc.HasLen, 1)
  2895  	c.Assert(machines[0].Machine, gc.Equals, "0/lxc/0")
  2896  }
  2897  
  2898  // updateConfig sets config variable with given key to a given value
  2899  // Asserts that no errors were encountered.
  2900  func (s *baseSuite) updateConfig(c *gc.C, key string, block bool) {
  2901  	err := s.State.UpdateEnvironConfig(map[string]interface{}{key: block}, nil, nil)
  2902  	c.Assert(err, jc.ErrorIsNil)
  2903  }
  2904  
  2905  func (s *clientSuite) TestClientAddMachinesWithConstraints(c *gc.C) {
  2906  	apiParams := make([]params.AddMachineParams, 3)
  2907  	for i := 0; i < 3; i++ {
  2908  		apiParams[i] = params.AddMachineParams{
  2909  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2910  		}
  2911  	}
  2912  	// The last machine has some constraints.
  2913  	apiParams[2].Constraints = constraints.MustParse("mem=4G")
  2914  	machines, err := s.APIState.Client().AddMachines(apiParams)
  2915  	c.Assert(err, jc.ErrorIsNil)
  2916  	c.Assert(len(machines), gc.Equals, 3)
  2917  	for i, machineResult := range machines {
  2918  		c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
  2919  		s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String())
  2920  	}
  2921  }
  2922  
  2923  func (s *clientSuite) TestClientAddMachinesWithPlacement(c *gc.C) {
  2924  	apiParams := make([]params.AddMachineParams, 4)
  2925  	for i := range apiParams {
  2926  		apiParams[i] = params.AddMachineParams{
  2927  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2928  		}
  2929  	}
  2930  	apiParams[0].Placement = instance.MustParsePlacement("lxc")
  2931  	apiParams[1].Placement = instance.MustParsePlacement("lxc:0")
  2932  	apiParams[1].ContainerType = instance.LXC
  2933  	apiParams[2].Placement = instance.MustParsePlacement("dummyenv:invalid")
  2934  	apiParams[3].Placement = instance.MustParsePlacement("dummyenv:valid")
  2935  	machines, err := s.APIState.Client().AddMachines(apiParams)
  2936  	c.Assert(err, jc.ErrorIsNil)
  2937  	c.Assert(len(machines), gc.Equals, 4)
  2938  	c.Assert(machines[0].Machine, gc.Equals, "0/lxc/0")
  2939  	c.Assert(machines[1].Error, gc.ErrorMatches, "container type and placement are mutually exclusive")
  2940  	c.Assert(machines[2].Error, gc.ErrorMatches, "cannot add a new machine: invalid placement is invalid")
  2941  	c.Assert(machines[3].Machine, gc.Equals, "1")
  2942  
  2943  	m, err := s.BackingState.Machine(machines[3].Machine)
  2944  	c.Assert(err, jc.ErrorIsNil)
  2945  	c.Assert(m.Placement(), gc.DeepEquals, apiParams[3].Placement.Directive)
  2946  }
  2947  
  2948  func (s *clientSuite) TestClientAddMachines1dot18(c *gc.C) {
  2949  	apiParams := make([]params.AddMachineParams, 2)
  2950  	for i := range apiParams {
  2951  		apiParams[i] = params.AddMachineParams{
  2952  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2953  		}
  2954  	}
  2955  	apiParams[1].ContainerType = instance.LXC
  2956  	apiParams[1].ParentId = "0"
  2957  	machines, err := s.APIState.Client().AddMachines1dot18(apiParams)
  2958  	c.Assert(err, jc.ErrorIsNil)
  2959  	c.Assert(len(machines), gc.Equals, 2)
  2960  	c.Assert(machines[0].Machine, gc.Equals, "0")
  2961  	c.Assert(machines[1].Machine, gc.Equals, "0/lxc/0")
  2962  }
  2963  
  2964  func (s *clientSuite) TestClientAddMachines1dot18SomeErrors(c *gc.C) {
  2965  	apiParams := []params.AddMachineParams{{
  2966  		Jobs:     []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2967  		ParentId: "123",
  2968  	}}
  2969  	machines, err := s.APIState.Client().AddMachines1dot18(apiParams)
  2970  	c.Assert(err, jc.ErrorIsNil)
  2971  	c.Assert(len(machines), gc.Equals, 1)
  2972  	c.Check(machines[0].Error, gc.ErrorMatches, "parent machine specified without container type")
  2973  }
  2974  
  2975  func (s *clientSuite) TestClientAddMachinesSomeErrors(c *gc.C) {
  2976  	// Here we check that adding a number of containers correctly handles the
  2977  	// case that some adds succeed and others fail and report the errors
  2978  	// accordingly.
  2979  	// We will set up params to the AddMachines API to attempt to create 3 machines.
  2980  	// Machines 0 and 1 will be added successfully.
  2981  	// Remaining machines will fail due to different reasons.
  2982  
  2983  	// Create a machine to host the requested containers.
  2984  	host, err := s.State.AddMachine("quantal", state.JobHostUnits)
  2985  	c.Assert(err, jc.ErrorIsNil)
  2986  	// The host only supports lxc containers.
  2987  	err = host.SetSupportedContainers([]instance.ContainerType{instance.LXC})
  2988  	c.Assert(err, jc.ErrorIsNil)
  2989  
  2990  	// Set up params for adding 3 containers.
  2991  	apiParams := make([]params.AddMachineParams, 3)
  2992  	for i := range apiParams {
  2993  		apiParams[i] = params.AddMachineParams{
  2994  			Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  2995  		}
  2996  	}
  2997  	// This will cause a machine add to fail due to an unsupported container.
  2998  	apiParams[2].ContainerType = instance.KVM
  2999  	apiParams[2].ParentId = host.Id()
  3000  	machines, err := s.APIState.Client().AddMachines(apiParams)
  3001  	c.Assert(err, jc.ErrorIsNil)
  3002  	c.Assert(len(machines), gc.Equals, 3)
  3003  
  3004  	// Check the results - machines 2 and 3 will have errors.
  3005  	c.Check(machines[0].Machine, gc.Equals, "1")
  3006  	c.Check(machines[0].Error, gc.IsNil)
  3007  	c.Check(machines[1].Machine, gc.Equals, "2")
  3008  	c.Check(machines[1].Error, gc.IsNil)
  3009  	c.Check(machines[2].Error, gc.ErrorMatches, "cannot add a new machine: machine 0 cannot host kvm containers")
  3010  }
  3011  
  3012  func (s *clientSuite) TestClientAddMachinesWithInstanceIdSomeErrors(c *gc.C) {
  3013  	apiParams := make([]params.AddMachineParams, 3)
  3014  	addrs := network.NewAddresses("1.2.3.4")
  3015  	hc := instance.MustParseHardware("mem=4G")
  3016  	for i := 0; i < 3; i++ {
  3017  		apiParams[i] = params.AddMachineParams{
  3018  			Jobs:       []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  3019  			InstanceId: instance.Id(fmt.Sprintf("1234-%d", i)),
  3020  			Nonce:      "foo",
  3021  			HardwareCharacteristics: hc,
  3022  			Addrs: params.FromNetworkAddresses(addrs),
  3023  		}
  3024  	}
  3025  	// This will cause the last machine add to fail.
  3026  	apiParams[2].Nonce = ""
  3027  	machines, err := s.APIState.Client().AddMachines(apiParams)
  3028  	c.Assert(err, jc.ErrorIsNil)
  3029  	c.Assert(len(machines), gc.Equals, 3)
  3030  	for i, machineResult := range machines {
  3031  		if i == 2 {
  3032  			c.Assert(machineResult.Error, gc.NotNil)
  3033  			c.Assert(machineResult.Error, gc.ErrorMatches, "cannot add a new machine: cannot add a machine with an instance id and no nonce")
  3034  		} else {
  3035  			c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i))
  3036  			s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String())
  3037  			instanceId := fmt.Sprintf("1234-%d", i)
  3038  			s.checkInstance(c, machineResult.Machine, instanceId, "foo", hc, addrs)
  3039  		}
  3040  	}
  3041  }
  3042  
  3043  func (s *clientSuite) checkInstance(c *gc.C, id, instanceId, nonce string,
  3044  	hc instance.HardwareCharacteristics, addr []network.Address) {
  3045  
  3046  	machine, err := s.BackingState.Machine(id)
  3047  	c.Assert(err, jc.ErrorIsNil)
  3048  	machineInstanceId, err := machine.InstanceId()
  3049  	c.Assert(err, jc.ErrorIsNil)
  3050  	c.Assert(machine.CheckProvisioned(nonce), jc.IsTrue)
  3051  	c.Assert(machineInstanceId, gc.Equals, instance.Id(instanceId))
  3052  	machineHardware, err := machine.HardwareCharacteristics()
  3053  	c.Assert(err, jc.ErrorIsNil)
  3054  	c.Assert(machineHardware.String(), gc.Equals, hc.String())
  3055  	c.Assert(machine.Addresses(), gc.DeepEquals, addr)
  3056  }
  3057  
  3058  func (s *clientSuite) TestInjectMachinesStillExists(c *gc.C) {
  3059  	results := new(params.AddMachinesResults)
  3060  	// We need to use Call directly because the client interface
  3061  	// no longer refers to InjectMachine.
  3062  	args := params.AddMachines{
  3063  		MachineParams: []params.AddMachineParams{{
  3064  			Jobs:       []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  3065  			InstanceId: "i-foo",
  3066  			Nonce:      "nonce",
  3067  		}},
  3068  	}
  3069  	err := s.APIState.APICall("Client", 0, "", "AddMachines", args, &results)
  3070  	c.Assert(err, jc.ErrorIsNil)
  3071  	c.Assert(results.Machines, gc.HasLen, 1)
  3072  }
  3073  
  3074  func (s *clientSuite) TestProvisioningScript(c *gc.C) {
  3075  	// Inject a machine and then call the ProvisioningScript API.
  3076  	// The result should be the same as when calling MachineConfig,
  3077  	// converting it to a cloudinit.MachineConfig, and disabling
  3078  	// apt_upgrade.
  3079  	apiParams := params.AddMachineParams{
  3080  		Jobs:       []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  3081  		InstanceId: instance.Id("1234"),
  3082  		Nonce:      "foo",
  3083  		HardwareCharacteristics: instance.MustParseHardware("arch=amd64"),
  3084  	}
  3085  	machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams})
  3086  	c.Assert(err, jc.ErrorIsNil)
  3087  	c.Assert(len(machines), gc.Equals, 1)
  3088  	machineId := machines[0].Machine
  3089  	// Call ProvisioningScript. Normally ProvisioningScript and
  3090  	// MachineConfig are mutually exclusive; both of them will
  3091  	// allocate a api password for the machine agent.
  3092  	script, err := s.APIState.Client().ProvisioningScript(params.ProvisioningScriptParams{
  3093  		MachineId: machineId,
  3094  		Nonce:     apiParams.Nonce,
  3095  	})
  3096  	c.Assert(err, jc.ErrorIsNil)
  3097  	icfg, err := client.InstanceConfig(s.State, machineId, apiParams.Nonce, "")
  3098  	c.Assert(err, jc.ErrorIsNil)
  3099  	provisioningScript, err := manual.ProvisioningScript(icfg)
  3100  	c.Assert(err, jc.ErrorIsNil)
  3101  	// ProvisioningScript internally calls MachineConfig,
  3102  	// which allocates a new, random password. Everything
  3103  	// about the scripts should be the same other than
  3104  	// the line containing "oldpassword" from agent.conf.
  3105  	scriptLines := strings.Split(script, "\n")
  3106  	provisioningScriptLines := strings.Split(provisioningScript, "\n")
  3107  	c.Assert(scriptLines, gc.HasLen, len(provisioningScriptLines))
  3108  	for i, line := range scriptLines {
  3109  		if strings.Contains(line, "oldpassword") {
  3110  			continue
  3111  		}
  3112  		c.Assert(line, gc.Equals, provisioningScriptLines[i])
  3113  	}
  3114  }
  3115  
  3116  func (s *clientSuite) TestProvisioningScriptDisablePackageCommands(c *gc.C) {
  3117  	apiParams := params.AddMachineParams{
  3118  		Jobs:       []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
  3119  		InstanceId: instance.Id("1234"),
  3120  		Nonce:      "foo",
  3121  		HardwareCharacteristics: instance.MustParseHardware("arch=amd64"),
  3122  	}
  3123  	machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams})
  3124  	c.Assert(err, jc.ErrorIsNil)
  3125  	c.Assert(len(machines), gc.Equals, 1)
  3126  	machineId := machines[0].Machine
  3127  
  3128  	provParams := params.ProvisioningScriptParams{
  3129  		MachineId: machineId,
  3130  		Nonce:     apiParams.Nonce,
  3131  	}
  3132  
  3133  	setUpdateBehavior := func(update, upgrade bool) {
  3134  		s.State.UpdateEnvironConfig(
  3135  			map[string]interface{}{
  3136  				"enable-os-upgrade":        upgrade,
  3137  				"enable-os-refresh-update": update,
  3138  			},
  3139  			nil,
  3140  			nil,
  3141  		)
  3142  	}
  3143  
  3144  	// Test enabling package commands
  3145  	provParams.DisablePackageCommands = false
  3146  	setUpdateBehavior(true, true)
  3147  	script, err := s.APIState.Client().ProvisioningScript(provParams)
  3148  	c.Assert(err, jc.ErrorIsNil)
  3149  	c.Check(script, jc.Contains, "apt-get update")
  3150  	c.Check(script, jc.Contains, "apt-get upgrade")
  3151  
  3152  	// Test disabling package commands
  3153  	provParams.DisablePackageCommands = true
  3154  	setUpdateBehavior(false, false)
  3155  	script, err = s.APIState.Client().ProvisioningScript(provParams)
  3156  	c.Assert(err, jc.ErrorIsNil)
  3157  	c.Check(script, gc.Not(jc.Contains), "apt-get update")
  3158  	c.Check(script, gc.Not(jc.Contains), "apt-get upgrade")
  3159  
  3160  	// Test client-specified DisablePackageCommands trumps environment
  3161  	// config variables.
  3162  	provParams.DisablePackageCommands = true
  3163  	setUpdateBehavior(true, true)
  3164  	script, err = s.APIState.Client().ProvisioningScript(provParams)
  3165  	c.Assert(err, jc.ErrorIsNil)
  3166  	c.Check(script, gc.Not(jc.Contains), "apt-get update")
  3167  	c.Check(script, gc.Not(jc.Contains), "apt-get upgrade")
  3168  
  3169  	// Test that in the abasence of a client-specified
  3170  	// DisablePackageCommands we use what's set in environments.yaml.
  3171  	provParams.DisablePackageCommands = false
  3172  	setUpdateBehavior(false, false)
  3173  	//provParams.UpdateBehavior = &params.UpdateBehavior{false, false}
  3174  	script, err = s.APIState.Client().ProvisioningScript(provParams)
  3175  	c.Assert(err, jc.ErrorIsNil)
  3176  	c.Check(script, gc.Not(jc.Contains), "apt-get update")
  3177  	c.Check(script, gc.Not(jc.Contains), "apt-get upgrade")
  3178  }
  3179  
  3180  type testModeCharmRepo struct {
  3181  	*charmrepo.CharmStore
  3182  	testMode bool
  3183  }
  3184  
  3185  // WithTestMode returns a repository Interface where test mode is enabled.
  3186  func (s *testModeCharmRepo) WithTestMode() charmrepo.Interface {
  3187  	s.testMode = true
  3188  	return s.CharmStore.WithTestMode()
  3189  }
  3190  
  3191  func (s *clientRepoSuite) TestClientSpecializeStoreOnDeployServiceSetCharmAndAddCharm(c *gc.C) {
  3192  	repo := &testModeCharmRepo{}
  3193  	s.PatchValue(&service.NewCharmStore, func(p charmrepo.NewCharmStoreParams) charmrepo.Interface {
  3194  		p.URL = s.Srv.URL()
  3195  		repo.CharmStore = charmrepo.NewCharmStore(p).(*charmrepo.CharmStore)
  3196  		return repo
  3197  	})
  3198  	attrs := map[string]interface{}{"test-mode": true}
  3199  	err := s.State.UpdateEnvironConfig(attrs, nil, nil)
  3200  	c.Assert(err, jc.ErrorIsNil)
  3201  
  3202  	// Check that the store's test mode is enabled when calling ServiceDeploy.
  3203  	curl, _ := s.UploadCharm(c, "trusty/dummy-1", "dummy")
  3204  	err = service.AddCharmWithAuthorization(s.State, params.AddCharmWithAuthorization{URL: curl.String()})
  3205  	c.Assert(err, jc.ErrorIsNil)
  3206  	err = s.APIState.Client().ServiceDeploy(
  3207  		curl.String(), "service", 3, "", constraints.Value{}, "",
  3208  	)
  3209  	c.Assert(err, jc.ErrorIsNil)
  3210  	c.Assert(repo.testMode, jc.IsTrue)
  3211  
  3212  	// Check that the store's test mode is enabled when calling ServiceSetCharm.
  3213  	curl, _ = s.UploadCharm(c, "trusty/wordpress-2", "wordpress")
  3214  	err = s.APIState.Client().ServiceSetCharm(
  3215  		"service", curl.String(), false,
  3216  	)
  3217  	c.Assert(repo.testMode, jc.IsTrue)
  3218  
  3219  	// Check that the store's test mode is enabled when calling AddCharm.
  3220  	curl, _ = s.UploadCharm(c, "utopic/riak-42", "riak")
  3221  	err = s.APIState.Client().AddCharm(curl)
  3222  	c.Assert(err, jc.ErrorIsNil)
  3223  	c.Assert(repo.testMode, jc.IsTrue)
  3224  }
  3225  
  3226  var resolveCharmTests = []struct {
  3227  	about      string
  3228  	url        string
  3229  	resolved   string
  3230  	parseErr   string
  3231  	resolveErr string
  3232  }{{
  3233  	about:    "wordpress resolved",
  3234  	url:      "cs:wordpress",
  3235  	resolved: "cs:trusty/wordpress",
  3236  }, {
  3237  	about:    "mysql resolved",
  3238  	url:      "cs:mysql",
  3239  	resolved: "cs:precise/mysql",
  3240  }, {
  3241  	about:    "riak resolved",
  3242  	url:      "cs:riak",
  3243  	resolved: "cs:trusty/riak",
  3244  }, {
  3245  	about:    "fully qualified char reference",
  3246  	url:      "cs:utopic/riak-5",
  3247  	resolved: "cs:utopic/riak-5",
  3248  }, {
  3249  	about:    "charm with series and no revision",
  3250  	url:      "cs:precise/wordpress",
  3251  	resolved: "cs:precise/wordpress",
  3252  }, {
  3253  	about:      "fully qualified reference not found",
  3254  	url:        "cs:utopic/riak-42",
  3255  	resolveErr: `cannot resolve charm URL "cs:utopic/riak-42": charm not found`,
  3256  }, {
  3257  	about:      "reference not found",
  3258  	url:        "cs:no-such",
  3259  	resolveErr: `cannot resolve charm URL "cs:no-such": charm not found`,
  3260  }, {
  3261  	about:    "invalid charm name",
  3262  	url:      "cs:",
  3263  	parseErr: `charm URL has invalid charm name: "cs:"`,
  3264  }, {
  3265  	about:      "local charm",
  3266  	url:        "local:wordpress",
  3267  	resolveErr: `only charm store charm references are supported, with cs: schema`,
  3268  }}
  3269  
  3270  func (s *clientRepoSuite) TestResolveCharm(c *gc.C) {
  3271  	// Add some charms to be resolved later.
  3272  	for _, url := range []string{
  3273  		"precise/wordpress-1",
  3274  		"trusty/wordpress-2",
  3275  		"precise/mysql-3",
  3276  		"trusty/riak-4",
  3277  		"utopic/riak-5",
  3278  	} {
  3279  		s.UploadCharm(c, url, "wordpress")
  3280  	}
  3281  
  3282  	// Run the tests.
  3283  	for i, test := range resolveCharmTests {
  3284  		c.Logf("test %d: %s", i, test.about)
  3285  
  3286  		client := s.APIState.Client()
  3287  		ref, err := charm.ParseReference(test.url)
  3288  		if test.parseErr == "" {
  3289  			if !c.Check(err, jc.ErrorIsNil) {
  3290  				continue
  3291  			}
  3292  		} else {
  3293  			c.Assert(err, gc.NotNil)
  3294  			c.Check(err, gc.ErrorMatches, test.parseErr)
  3295  			continue
  3296  		}
  3297  
  3298  		curl, err := client.ResolveCharm(ref)
  3299  		if test.resolveErr == "" {
  3300  			c.Assert(err, jc.ErrorIsNil)
  3301  			c.Check(curl.String(), gc.Equals, test.resolved)
  3302  			continue
  3303  		}
  3304  		c.Check(err, gc.ErrorMatches, test.resolveErr)
  3305  		c.Check(curl, gc.IsNil)
  3306  	}
  3307  }
  3308  
  3309  func (s *clientSuite) TestRetryProvisioning(c *gc.C) {
  3310  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  3311  	c.Assert(err, jc.ErrorIsNil)
  3312  	err = machine.SetStatus(state.StatusError, "error", nil)
  3313  	c.Assert(err, jc.ErrorIsNil)
  3314  	_, err = s.APIState.Client().RetryProvisioning(machine.Tag().(names.MachineTag))
  3315  	c.Assert(err, jc.ErrorIsNil)
  3316  
  3317  	statusInfo, err := machine.Status()
  3318  	c.Assert(err, jc.ErrorIsNil)
  3319  	c.Assert(statusInfo.Status, gc.Equals, state.StatusError)
  3320  	c.Assert(statusInfo.Message, gc.Equals, "error")
  3321  	c.Assert(statusInfo.Data["transient"], jc.IsTrue)
  3322  }
  3323  
  3324  func (s *clientSuite) setupRetryProvisioning(c *gc.C) *state.Machine {
  3325  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  3326  	c.Assert(err, jc.ErrorIsNil)
  3327  	err = machine.SetStatus(state.StatusError, "error", nil)
  3328  	c.Assert(err, jc.ErrorIsNil)
  3329  	return machine
  3330  }
  3331  
  3332  func (s *clientSuite) assertRetryProvisioning(c *gc.C, machine *state.Machine) {
  3333  	_, err := s.APIState.Client().RetryProvisioning(machine.Tag().(names.MachineTag))
  3334  	c.Assert(err, jc.ErrorIsNil)
  3335  	statusInfo, err := machine.Status()
  3336  	c.Assert(err, jc.ErrorIsNil)
  3337  	c.Assert(statusInfo.Status, gc.Equals, state.StatusError)
  3338  	c.Assert(statusInfo.Message, gc.Equals, "error")
  3339  	c.Assert(statusInfo.Data["transient"], jc.IsTrue)
  3340  }
  3341  
  3342  func (s *clientSuite) assertRetryProvisioningBlocked(c *gc.C, machine *state.Machine, msg string) {
  3343  	_, err := s.APIState.Client().RetryProvisioning(machine.Tag().(names.MachineTag))
  3344  	s.AssertBlocked(c, err, msg)
  3345  }
  3346  
  3347  func (s *clientSuite) TestBlockDestroyRetryProvisioning(c *gc.C) {
  3348  	m := s.setupRetryProvisioning(c)
  3349  	s.BlockDestroyEnvironment(c, "TestBlockDestroyRetryProvisioning")
  3350  	s.assertRetryProvisioning(c, m)
  3351  }
  3352  
  3353  func (s *clientSuite) TestBlockRemoveRetryProvisioning(c *gc.C) {
  3354  	m := s.setupRetryProvisioning(c)
  3355  	s.BlockRemoveObject(c, "TestBlockRemoveRetryProvisioning")
  3356  	s.assertRetryProvisioning(c, m)
  3357  }
  3358  
  3359  func (s *clientSuite) TestBlockChangesRetryProvisioning(c *gc.C) {
  3360  	m := s.setupRetryProvisioning(c)
  3361  	s.BlockAllChanges(c, "TestBlockChangesRetryProvisioning")
  3362  	s.assertRetryProvisioningBlocked(c, m, "TestBlockChangesRetryProvisioning")
  3363  }
  3364  
  3365  func (s *clientSuite) TestAPIHostPorts(c *gc.C) {
  3366  	server1Addresses := []network.Address{{
  3367  		Value: "server-1",
  3368  		Type:  network.HostName,
  3369  		Scope: network.ScopePublic,
  3370  	}, {
  3371  		Value:       "10.0.0.1",
  3372  		Type:        network.IPv4Address,
  3373  		NetworkName: "internal",
  3374  		Scope:       network.ScopeCloudLocal,
  3375  	}}
  3376  	server2Addresses := []network.Address{{
  3377  		Value:       "::1",
  3378  		Type:        network.IPv6Address,
  3379  		NetworkName: "loopback",
  3380  		Scope:       network.ScopeMachineLocal,
  3381  	}}
  3382  	stateAPIHostPorts := [][]network.HostPort{
  3383  		network.AddressesWithPort(server1Addresses, 123),
  3384  		network.AddressesWithPort(server2Addresses, 456),
  3385  	}
  3386  
  3387  	err := s.State.SetAPIHostPorts(stateAPIHostPorts)
  3388  	c.Assert(err, jc.ErrorIsNil)
  3389  	apiHostPorts, err := s.APIState.Client().APIHostPorts()
  3390  	c.Assert(err, jc.ErrorIsNil)
  3391  	c.Assert(apiHostPorts, gc.DeepEquals, stateAPIHostPorts)
  3392  }
  3393  
  3394  func (s *clientSuite) TestClientAgentVersion(c *gc.C) {
  3395  	current := version.MustParse("1.2.0")
  3396  	s.PatchValue(&version.Current.Number, current)
  3397  	result, err := s.APIState.Client().AgentVersion()
  3398  	c.Assert(err, jc.ErrorIsNil)
  3399  	c.Assert(result, gc.Equals, current)
  3400  }
  3401  
  3402  func (s *serverSuite) TestBlockServiceDestroy(c *gc.C) {
  3403  	s.AddTestingService(c, "dummy-service", s.AddTestingCharm(c, "dummy"))
  3404  
  3405  	// block remove-objects
  3406  	s.BlockRemoveObject(c, "TestBlockServiceDestroy")
  3407  	err := s.APIState.Client().ServiceDestroy("dummy-service")
  3408  	s.AssertBlocked(c, err, "TestBlockServiceDestroy")
  3409  	// Tests may have invalid service names.
  3410  	service, err := s.State.Service("dummy-service")
  3411  	if err == nil {
  3412  		// For valid service names, check that service is alive :-)
  3413  		assertLife(c, service, state.Alive)
  3414  	}
  3415  }
  3416  
  3417  func (s *clientSuite) assertDestroyMachineSuccess(c *gc.C, u *state.Unit, m0, m1, m2 *state.Machine) {
  3418  	err := s.APIState.Client().DestroyMachines("0", "1", "2")
  3419  	c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment; machine 1 has unit "wordpress/0" assigned`)
  3420  	assertLife(c, m0, state.Alive)
  3421  	assertLife(c, m1, state.Alive)
  3422  	assertLife(c, m2, state.Dying)
  3423  
  3424  	err = u.UnassignFromMachine()
  3425  	c.Assert(err, jc.ErrorIsNil)
  3426  	err = s.APIState.Client().DestroyMachines("0", "1", "2")
  3427  	c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment`)
  3428  	assertLife(c, m0, state.Alive)
  3429  	assertLife(c, m1, state.Dying)
  3430  	assertLife(c, m2, state.Dying)
  3431  }
  3432  
  3433  func (s *clientSuite) assertBlockedErrorAndLiveliness(
  3434  	c *gc.C,
  3435  	err error,
  3436  	msg string,
  3437  	living1 state.Living,
  3438  	living2 state.Living,
  3439  	living3 state.Living,
  3440  	living4 state.Living,
  3441  ) {
  3442  	s.AssertBlocked(c, err, msg)
  3443  	assertLife(c, living1, state.Alive)
  3444  	assertLife(c, living2, state.Alive)
  3445  	assertLife(c, living3, state.Alive)
  3446  	assertLife(c, living4, state.Alive)
  3447  }
  3448  
  3449  func (s *clientSuite) TestBlockRemoveDestroyMachines(c *gc.C) {
  3450  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
  3451  	s.BlockRemoveObject(c, "TestBlockRemoveDestroyMachines")
  3452  	err := s.APIState.Client().DestroyMachines("0", "1", "2")
  3453  	s.assertBlockedErrorAndLiveliness(c, err, "TestBlockRemoveDestroyMachines", m0, m1, m2, u)
  3454  }
  3455  
  3456  func (s *clientSuite) TestBlockChangesDestroyMachines(c *gc.C) {
  3457  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
  3458  	s.BlockAllChanges(c, "TestBlockChangesDestroyMachines")
  3459  	err := s.APIState.Client().DestroyMachines("0", "1", "2")
  3460  	s.assertBlockedErrorAndLiveliness(c, err, "TestBlockChangesDestroyMachines", m0, m1, m2, u)
  3461  }
  3462  
  3463  func (s *clientSuite) TestBlockDestoryDestroyMachines(c *gc.C) {
  3464  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
  3465  	s.BlockDestroyEnvironment(c, "TestBlockDestoryDestroyMachines")
  3466  	s.assertDestroyMachineSuccess(c, u, m0, m1, m2)
  3467  }
  3468  
  3469  func (s *clientSuite) TestAnyBlockForceDestroyMachines(c *gc.C) {
  3470  	// force bypasses all blocks
  3471  	s.BlockAllChanges(c, "TestAnyBlockForceDestroyMachines")
  3472  	s.BlockDestroyEnvironment(c, "TestAnyBlockForceDestroyMachines")
  3473  	s.BlockRemoveObject(c, "TestAnyBlockForceDestroyMachines")
  3474  	s.assertForceDestroyMachines(c)
  3475  }
  3476  
  3477  func (s *clientSuite) assertForceDestroyMachines(c *gc.C) {
  3478  	m0, m1, m2, u := s.setupDestroyMachinesTest(c)
  3479  
  3480  	err := s.APIState.Client().ForceDestroyMachines("0", "1", "2")
  3481  	c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment`)
  3482  	assertLife(c, m0, state.Alive)
  3483  	assertLife(c, m1, state.Alive)
  3484  	assertLife(c, m2, state.Alive)
  3485  	assertLife(c, u, state.Alive)
  3486  
  3487  	err = s.State.Cleanup()
  3488  	c.Assert(err, jc.ErrorIsNil)
  3489  	assertLife(c, m0, state.Alive)
  3490  	assertLife(c, m1, state.Dead)
  3491  	assertLife(c, m2, state.Dead)
  3492  	assertRemoved(c, u)
  3493  }
  3494  
  3495  func (s *clientSuite) assertDestroyPrincipalUnits(c *gc.C, units []*state.Unit) {
  3496  	// Destroy 2 of them; check they become Dying.
  3497  	err := s.APIState.Client().DestroyServiceUnits("wordpress/0", "wordpress/1")
  3498  	c.Assert(err, jc.ErrorIsNil)
  3499  	assertLife(c, units[0], state.Dying)
  3500  	assertLife(c, units[1], state.Dying)
  3501  
  3502  	// Try to destroy an Alive one and a Dying one; check
  3503  	// it destroys the Alive one and ignores the Dying one.
  3504  	err = s.APIState.Client().DestroyServiceUnits("wordpress/2", "wordpress/0")
  3505  	c.Assert(err, jc.ErrorIsNil)
  3506  	assertLife(c, units[2], state.Dying)
  3507  
  3508  	// Try to destroy an Alive one along with a nonexistent one; check that
  3509  	// the valid instruction is followed but the invalid one is warned about.
  3510  	err = s.APIState.Client().DestroyServiceUnits("boojum/123", "wordpress/3")
  3511  	c.Assert(err, gc.ErrorMatches, `some units were not destroyed: unit "boojum/123" does not exist`)
  3512  	assertLife(c, units[3], state.Dying)
  3513  
  3514  	// Make one Dead, and destroy an Alive one alongside it; check no errors.
  3515  	wp0, err := s.State.Unit("wordpress/0")
  3516  	c.Assert(err, jc.ErrorIsNil)
  3517  	err = wp0.EnsureDead()
  3518  	c.Assert(err, jc.ErrorIsNil)
  3519  	err = s.APIState.Client().DestroyServiceUnits("wordpress/0", "wordpress/4")
  3520  	c.Assert(err, jc.ErrorIsNil)
  3521  	assertLife(c, units[0], state.Dead)
  3522  	assertLife(c, units[4], state.Dying)
  3523  }
  3524  
  3525  func (s *clientSuite) setupDestroyPrincipalUnits(c *gc.C) []*state.Unit {
  3526  	units := make([]*state.Unit, 5)
  3527  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  3528  	for i := range units {
  3529  		unit, err := wordpress.AddUnit()
  3530  		c.Assert(err, jc.ErrorIsNil)
  3531  		err = unit.SetAgentStatus(state.StatusIdle, "", nil)
  3532  		c.Assert(err, jc.ErrorIsNil)
  3533  		units[i] = unit
  3534  	}
  3535  	return units
  3536  }
  3537  func (s *clientSuite) TestBlockChangesDestroyPrincipalUnits(c *gc.C) {
  3538  	units := s.setupDestroyPrincipalUnits(c)
  3539  	s.BlockAllChanges(c, "TestBlockChangesDestroyPrincipalUnits")
  3540  	err := s.APIState.Client().DestroyServiceUnits("wordpress/0", "wordpress/1")
  3541  	s.assertBlockedErrorAndLiveliness(c, err, "TestBlockChangesDestroyPrincipalUnits", units[0], units[1], units[2], units[3])
  3542  }
  3543  
  3544  func (s *clientSuite) TestBlockRemoveDestroyPrincipalUnits(c *gc.C) {
  3545  	units := s.setupDestroyPrincipalUnits(c)
  3546  	s.BlockRemoveObject(c, "TestBlockRemoveDestroyPrincipalUnits")
  3547  	err := s.APIState.Client().DestroyServiceUnits("wordpress/0", "wordpress/1")
  3548  	s.assertBlockedErrorAndLiveliness(c, err, "TestBlockRemoveDestroyPrincipalUnits", units[0], units[1], units[2], units[3])
  3549  }
  3550  
  3551  func (s *clientSuite) TestBlockDestroyDestroyPrincipalUnits(c *gc.C) {
  3552  	units := s.setupDestroyPrincipalUnits(c)
  3553  	s.BlockDestroyEnvironment(c, "TestBlockDestroyDestroyPrincipalUnits")
  3554  	err := s.APIState.Client().DestroyServiceUnits("wordpress/0", "wordpress/1")
  3555  	c.Assert(err, jc.ErrorIsNil)
  3556  	assertLife(c, units[0], state.Dying)
  3557  	assertLife(c, units[1], state.Dying)
  3558  }
  3559  
  3560  func (s *clientSuite) assertDestroySubordinateUnits(c *gc.C, wordpress0, logging0 *state.Unit) {
  3561  	// Try to destroy the principal and the subordinate together; check it warns
  3562  	// about the subordinate, but destroys the one it can. (The principal unit
  3563  	// agent will be resposible for destroying the subordinate.)
  3564  	err := s.APIState.Client().DestroyServiceUnits("wordpress/0", "logging/0")
  3565  	c.Assert(err, gc.ErrorMatches, `some units were not destroyed: unit "logging/0" is a subordinate`)
  3566  	assertLife(c, wordpress0, state.Dying)
  3567  	assertLife(c, logging0, state.Alive)
  3568  }
  3569  
  3570  func (s *clientSuite) TestBlockRemoveDestroySubordinateUnits(c *gc.C) {
  3571  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  3572  	wordpress0, err := wordpress.AddUnit()
  3573  	c.Assert(err, jc.ErrorIsNil)
  3574  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
  3575  	eps, err := s.State.InferEndpoints("logging", "wordpress")
  3576  	c.Assert(err, jc.ErrorIsNil)
  3577  	rel, err := s.State.AddRelation(eps...)
  3578  	c.Assert(err, jc.ErrorIsNil)
  3579  	ru, err := rel.Unit(wordpress0)
  3580  	c.Assert(err, jc.ErrorIsNil)
  3581  	err = ru.EnterScope(nil)
  3582  	c.Assert(err, jc.ErrorIsNil)
  3583  	logging0, err := s.State.Unit("logging/0")
  3584  	c.Assert(err, jc.ErrorIsNil)
  3585  
  3586  	s.BlockRemoveObject(c, "TestBlockRemoveDestroySubordinateUnits")
  3587  	// Try to destroy the subordinate alone; check it fails.
  3588  	err = s.APIState.Client().DestroyServiceUnits("logging/0")
  3589  	s.AssertBlocked(c, err, "TestBlockRemoveDestroySubordinateUnits")
  3590  	assertLife(c, rel, state.Alive)
  3591  	assertLife(c, wordpress0, state.Alive)
  3592  	assertLife(c, logging0, state.Alive)
  3593  
  3594  	err = s.APIState.Client().DestroyServiceUnits("wordpress/0", "logging/0")
  3595  	s.AssertBlocked(c, err, "TestBlockRemoveDestroySubordinateUnits")
  3596  	assertLife(c, wordpress0, state.Alive)
  3597  	assertLife(c, logging0, state.Alive)
  3598  	assertLife(c, rel, state.Alive)
  3599  }
  3600  
  3601  func (s *clientSuite) TestBlockChangesDestroySubordinateUnits(c *gc.C) {
  3602  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  3603  	wordpress0, err := wordpress.AddUnit()
  3604  	c.Assert(err, jc.ErrorIsNil)
  3605  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
  3606  	eps, err := s.State.InferEndpoints("logging", "wordpress")
  3607  	c.Assert(err, jc.ErrorIsNil)
  3608  	rel, err := s.State.AddRelation(eps...)
  3609  	c.Assert(err, jc.ErrorIsNil)
  3610  	ru, err := rel.Unit(wordpress0)
  3611  	c.Assert(err, jc.ErrorIsNil)
  3612  	err = ru.EnterScope(nil)
  3613  	c.Assert(err, jc.ErrorIsNil)
  3614  	logging0, err := s.State.Unit("logging/0")
  3615  	c.Assert(err, jc.ErrorIsNil)
  3616  
  3617  	s.BlockAllChanges(c, "TestBlockChangesDestroySubordinateUnits")
  3618  	// Try to destroy the subordinate alone; check it fails.
  3619  	err = s.APIState.Client().DestroyServiceUnits("logging/0")
  3620  	s.AssertBlocked(c, err, "TestBlockChangesDestroySubordinateUnits")
  3621  	assertLife(c, rel, state.Alive)
  3622  	assertLife(c, wordpress0, state.Alive)
  3623  	assertLife(c, logging0, state.Alive)
  3624  
  3625  	err = s.APIState.Client().DestroyServiceUnits("wordpress/0", "logging/0")
  3626  	s.AssertBlocked(c, err, "TestBlockChangesDestroySubordinateUnits")
  3627  	assertLife(c, wordpress0, state.Alive)
  3628  	assertLife(c, logging0, state.Alive)
  3629  	assertLife(c, rel, state.Alive)
  3630  }
  3631  
  3632  func (s *clientSuite) TestBlockDestroyDestroySubordinateUnits(c *gc.C) {
  3633  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  3634  	wordpress0, err := wordpress.AddUnit()
  3635  	c.Assert(err, jc.ErrorIsNil)
  3636  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
  3637  	eps, err := s.State.InferEndpoints("logging", "wordpress")
  3638  	c.Assert(err, jc.ErrorIsNil)
  3639  	rel, err := s.State.AddRelation(eps...)
  3640  	c.Assert(err, jc.ErrorIsNil)
  3641  	ru, err := rel.Unit(wordpress0)
  3642  	c.Assert(err, jc.ErrorIsNil)
  3643  	err = ru.EnterScope(nil)
  3644  	c.Assert(err, jc.ErrorIsNil)
  3645  	logging0, err := s.State.Unit("logging/0")
  3646  	c.Assert(err, jc.ErrorIsNil)
  3647  
  3648  	s.BlockDestroyEnvironment(c, "TestBlockDestroyDestroySubordinateUnits")
  3649  	// Try to destroy the subordinate alone; check it fails.
  3650  	err = s.APIState.Client().DestroyServiceUnits("logging/0")
  3651  	c.Assert(err, gc.ErrorMatches, `no units were destroyed: unit "logging/0" is a subordinate`)
  3652  	assertLife(c, logging0, state.Alive)
  3653  
  3654  	s.assertDestroySubordinateUnits(c, wordpress0, logging0)
  3655  }
  3656  
  3657  func (s *clientSuite) TestBlockRemoveDestroyRelation(c *gc.C) {
  3658  	endpoints := []string{"wordpress", "mysql"}
  3659  	relation := s.setupRelationScenario(c, endpoints)
  3660  	// block remove-objects
  3661  	s.BlockRemoveObject(c, "TestBlockRemoveDestroyRelation")
  3662  	err := s.APIState.Client().DestroyRelation(endpoints...)
  3663  	s.AssertBlocked(c, err, "TestBlockRemoveDestroyRelation")
  3664  	assertLife(c, relation, state.Alive)
  3665  }
  3666  
  3667  func (s *clientSuite) TestBlockChangeDestroyRelation(c *gc.C) {
  3668  	endpoints := []string{"wordpress", "mysql"}
  3669  	relation := s.setupRelationScenario(c, endpoints)
  3670  	s.BlockAllChanges(c, "TestBlockChangeDestroyRelation")
  3671  	err := s.APIState.Client().DestroyRelation(endpoints...)
  3672  	s.AssertBlocked(c, err, "TestBlockChangeDestroyRelation")
  3673  	assertLife(c, relation, state.Alive)
  3674  }
  3675  
  3676  func (s *clientSuite) TestBlockDestroyDestroyRelation(c *gc.C) {
  3677  	s.BlockDestroyEnvironment(c, "TestBlockDestroyDestroyRelation")
  3678  	endpoints := []string{"wordpress", "mysql"}
  3679  	s.assertDestroyRelation(c, endpoints)
  3680  }