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