github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/service_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state_test
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    12  	jc "github.com/juju/testing/checkers"
    13  	jujutxn "github.com/juju/txn"
    14  	"github.com/juju/utils/set"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/charm.v6-unstable"
    17  	"gopkg.in/mgo.v2"
    18  	"gopkg.in/mgo.v2/bson"
    19  	"gopkg.in/mgo.v2/txn"
    20  
    21  	"github.com/juju/juju/constraints"
    22  	"github.com/juju/juju/environs/config"
    23  	"github.com/juju/juju/state"
    24  	"github.com/juju/juju/state/testing"
    25  	"github.com/juju/juju/status"
    26  	"github.com/juju/juju/storage/provider"
    27  	"github.com/juju/juju/storage/provider/registry"
    28  )
    29  
    30  type ServiceSuite struct {
    31  	ConnSuite
    32  	charm *state.Charm
    33  	mysql *state.Service
    34  }
    35  
    36  var _ = gc.Suite(&ServiceSuite{})
    37  
    38  func (s *ServiceSuite) SetUpTest(c *gc.C) {
    39  	s.ConnSuite.SetUpTest(c)
    40  	s.policy.GetConstraintsValidator = func(*config.Config, state.SupportedArchitecturesQuerier) (constraints.Validator, error) {
    41  		validator := constraints.NewValidator()
    42  		validator.RegisterConflicts([]string{constraints.InstanceType}, []string{constraints.Mem})
    43  		validator.RegisterUnsupported([]string{constraints.CpuPower})
    44  		return validator, nil
    45  	}
    46  	s.charm = s.AddTestingCharm(c, "mysql")
    47  	s.mysql = s.AddTestingService(c, "mysql", s.charm)
    48  }
    49  
    50  func (s *ServiceSuite) TestSetCharm(c *gc.C) {
    51  	ch, force, err := s.mysql.Charm()
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	c.Assert(ch.URL(), gc.DeepEquals, s.charm.URL())
    54  	c.Assert(force, jc.IsFalse)
    55  	url, force := s.mysql.CharmURL()
    56  	c.Assert(url, gc.DeepEquals, s.charm.URL())
    57  	c.Assert(force, jc.IsFalse)
    58  
    59  	// Add a compatible charm and force it.
    60  	sch := s.AddMetaCharm(c, "mysql", metaBase, 2)
    61  
    62  	cfg := state.SetCharmConfig{
    63  		Charm:      sch,
    64  		ForceUnits: true,
    65  	}
    66  	err = s.mysql.SetCharm(cfg)
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	ch, force, err = s.mysql.Charm()
    69  	c.Assert(err, jc.ErrorIsNil)
    70  	c.Assert(ch.URL(), gc.DeepEquals, sch.URL())
    71  	c.Assert(force, jc.IsTrue)
    72  	url, force = s.mysql.CharmURL()
    73  	c.Assert(url, gc.DeepEquals, sch.URL())
    74  	c.Assert(force, jc.IsTrue)
    75  }
    76  
    77  func (s *ServiceSuite) TestSetCharmLegacy(c *gc.C) {
    78  	chDifferentSeries := state.AddTestingCharmForSeries(c, s.State, "precise", "mysql")
    79  
    80  	cfg := state.SetCharmConfig{
    81  		Charm:       chDifferentSeries,
    82  		ForceSeries: true,
    83  	}
    84  	err := s.mysql.SetCharm(cfg)
    85  	c.Assert(err, gc.ErrorMatches, "cannot change a service's series")
    86  }
    87  
    88  func (s *ServiceSuite) TestClientServiceSetCharmUnsupportedSeries(c *gc.C) {
    89  	ch := state.AddTestingCharmMultiSeries(c, s.State, "multi-series")
    90  	svc := state.AddTestingServiceForSeries(c, s.State, "precise", "service", ch, s.Owner)
    91  
    92  	chDifferentSeries := state.AddTestingCharmMultiSeries(c, s.State, "multi-series2")
    93  	cfg := state.SetCharmConfig{
    94  		Charm: chDifferentSeries,
    95  	}
    96  	err := svc.SetCharm(cfg)
    97  	c.Assert(err, gc.ErrorMatches, "cannot upgrade charm, only these series are supported: trusty, wily")
    98  }
    99  
   100  func (s *ServiceSuite) TestClientServiceSetCharmUnsupportedSeriesForce(c *gc.C) {
   101  	ch := state.AddTestingCharmMultiSeries(c, s.State, "multi-series")
   102  	svc := state.AddTestingServiceForSeries(c, s.State, "precise", "service", ch, s.Owner)
   103  
   104  	chDifferentSeries := state.AddTestingCharmMultiSeries(c, s.State, "multi-series2")
   105  	cfg := state.SetCharmConfig{
   106  		Charm:       chDifferentSeries,
   107  		ForceSeries: true,
   108  	}
   109  	err := svc.SetCharm(cfg)
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	svc, err = s.State.Service("service")
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	ch, _, err = svc.Charm()
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	c.Assert(ch.URL().String(), gc.Equals, "cs:multi-series2-8")
   116  }
   117  
   118  func (s *ServiceSuite) TestClientServiceSetCharmWrongOS(c *gc.C) {
   119  	ch := state.AddTestingCharmMultiSeries(c, s.State, "multi-series")
   120  	svc := state.AddTestingServiceForSeries(c, s.State, "precise", "service", ch, s.Owner)
   121  
   122  	chDifferentSeries := state.AddTestingCharmMultiSeries(c, s.State, "multi-series-windows")
   123  	cfg := state.SetCharmConfig{
   124  		Charm:       chDifferentSeries,
   125  		ForceSeries: true,
   126  	}
   127  	err := svc.SetCharm(cfg)
   128  	c.Assert(err, gc.ErrorMatches, `cannot upgrade charm, OS "Ubuntu" not supported by charm`)
   129  }
   130  
   131  func (s *ServiceSuite) TestSetCharmPreconditions(c *gc.C) {
   132  	logging := s.AddTestingCharm(c, "logging")
   133  	cfg := state.SetCharmConfig{Charm: logging}
   134  	err := s.mysql.SetCharm(cfg)
   135  	c.Assert(err, gc.ErrorMatches, "cannot change a service's subordinacy")
   136  
   137  	othermysql := s.AddSeriesCharm(c, "mysql", "otherseries")
   138  	cfg2 := state.SetCharmConfig{Charm: othermysql}
   139  	err = s.mysql.SetCharm(cfg2)
   140  	c.Assert(err, gc.ErrorMatches, "cannot change a service's series")
   141  }
   142  
   143  func (s *ServiceSuite) TestSetCharmUpdatesBindings(c *gc.C) {
   144  	_, err := s.State.AddSpace("db", "", nil, false)
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	_, err = s.State.AddSpace("client", "", nil, true)
   147  	c.Assert(err, jc.ErrorIsNil)
   148  	oldCharm := s.AddMetaCharm(c, "mysql", metaBase, 44)
   149  
   150  	service, err := s.State.AddService(state.AddServiceArgs{
   151  		Name:  "yoursql",
   152  		Owner: s.Owner.String(),
   153  		Charm: oldCharm,
   154  		EndpointBindings: map[string]string{
   155  			"server": "db",
   156  			"client": "client",
   157  		}})
   158  	c.Assert(err, jc.ErrorIsNil)
   159  
   160  	newCharm := s.AddMetaCharm(c, "mysql", metaExtraEndpoints, 43)
   161  	cfg := state.SetCharmConfig{Charm: newCharm}
   162  	err = service.SetCharm(cfg)
   163  	c.Assert(err, jc.ErrorIsNil)
   164  	updatedBindings, err := service.EndpointBindings()
   165  	c.Assert(err, jc.ErrorIsNil)
   166  	c.Assert(updatedBindings, jc.DeepEquals, map[string]string{
   167  		// Existing bindings are preserved.
   168  		"server":  "db",
   169  		"client":  "client",
   170  		"cluster": "", // inherited from defaults in AddService.
   171  		// New endpoints use empty defaults.
   172  		"foo":  "",
   173  		"baz":  "",
   174  		"just": "",
   175  	})
   176  }
   177  
   178  func (s *ServiceSuite) TestSetCharmWithWeirdlyNamedEndpoints(c *gc.C) {
   179  	// This test ensures if special characters appear in endpoint names of the
   180  	// charm metadata, they are properly escaped before saving to mongo, and
   181  	// unescaped when read back.
   182  	_, err := s.State.AddSpace("client", "", nil, true)
   183  	c.Assert(err, jc.ErrorIsNil)
   184  	_, err = s.State.AddSpace("db", "", nil, false)
   185  	c.Assert(err, jc.ErrorIsNil)
   186  
   187  	initialBindings := map[string]string{
   188  		"$pull":     "db",
   189  		"$set.foo":  "",
   190  		"cli ent .": "client",
   191  		".":         "db",
   192  	}
   193  	weirdOldCharm := s.AddMetaCharm(c, "mysql", `
   194  name: mysql
   195  summary: "Fake MySQL Database engine"
   196  description: "Complete with nonsense relations"
   197  provides:
   198    $pull: mysql
   199    $set.foo: bar
   200  requires:
   201    foo: something
   202    "cli ent .": mysql
   203  peers:
   204    ".": bad
   205    "$": mysql
   206  `, 42)
   207  	weirdNewCharm := s.AddMetaCharm(c, "mysql", `
   208  name: mysql
   209  summary: "Fake MySQL Database engine"
   210  description: "Complete with nonsense relations"
   211  provides:
   212    ser$ver2: mysql
   213    $pull: mysql
   214    $set.foo: bar
   215  requires:
   216    "cli ent 2": mysql
   217  peers:
   218    "$": mysql
   219    ".": bad
   220  `, 43)
   221  
   222  	weirdService := s.AddTestingServiceWithBindings(c, "weird", weirdOldCharm, initialBindings)
   223  	readBindings, err := weirdService.EndpointBindings()
   224  	c.Assert(err, jc.ErrorIsNil)
   225  	expectedBindings := map[string]string{
   226  		"cli ent .": "client",
   227  		"foo":       "",
   228  		"$":         "",
   229  		".":         "db",
   230  		"$set.foo":  "",
   231  		"$pull":     "db",
   232  	}
   233  	c.Check(readBindings, jc.DeepEquals, expectedBindings)
   234  
   235  	cfg := state.SetCharmConfig{Charm: weirdNewCharm}
   236  	err = weirdService.SetCharm(cfg)
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	readBindings, err = weirdService.EndpointBindings()
   239  	c.Assert(err, jc.ErrorIsNil)
   240  
   241  	expectedBindings = map[string]string{
   242  		"ser$ver2":  "",
   243  		"cli ent 2": "",
   244  		"$":         "",
   245  		".":         "db",
   246  		"$set.foo":  "",
   247  		"$pull":     "db",
   248  	}
   249  	c.Check(readBindings, jc.DeepEquals, expectedBindings)
   250  }
   251  
   252  var metaBase = `
   253  name: mysql
   254  summary: "Fake MySQL Database engine"
   255  description: "Complete with nonsense relations"
   256  provides:
   257    server: mysql
   258  requires:
   259    client: mysql
   260  peers:
   261    cluster: mysql
   262  `
   263  var metaDifferentProvider = `
   264  name: mysql
   265  description: none
   266  summary: none
   267  provides:
   268    kludge: mysql
   269  requires:
   270    client: mysql
   271  peers:
   272    cluster: mysql
   273  `
   274  var metaDifferentRequirer = `
   275  name: mysql
   276  description: none
   277  summary: none
   278  provides:
   279    server: mysql
   280  requires:
   281    kludge: mysql
   282  peers:
   283    cluster: mysql
   284  `
   285  var metaDifferentPeer = `
   286  name: mysql
   287  description: none
   288  summary: none
   289  provides:
   290    server: mysql
   291  requires:
   292    client: mysql
   293  peers:
   294    kludge: mysql
   295  `
   296  var metaExtraEndpoints = `
   297  name: mysql
   298  description: none
   299  summary: none
   300  provides:
   301    server: mysql
   302    foo: bar
   303  requires:
   304    client: mysql
   305    baz: woot
   306  peers:
   307    cluster: mysql
   308    just: me
   309  `
   310  
   311  var setCharmEndpointsTests = []struct {
   312  	summary string
   313  	meta    string
   314  	err     string
   315  }{{
   316  	summary: "different provider (but no relation yet)",
   317  	meta:    metaDifferentProvider,
   318  }, {
   319  	summary: "different requirer (but no relation yet)",
   320  	meta:    metaDifferentRequirer,
   321  }, {
   322  	summary: "different peer",
   323  	meta:    metaDifferentPeer,
   324  	err:     `cannot upgrade service "fakemysql" to charm "local:quantal/quantal-mysql-5": would break relation "fakemysql:cluster"`,
   325  }, {
   326  	summary: "same relations ok",
   327  	meta:    metaBase,
   328  }, {
   329  	summary: "extra endpoints ok",
   330  	meta:    metaExtraEndpoints,
   331  }}
   332  
   333  func (s *ServiceSuite) TestSetCharmChecksEndpointsWithoutRelations(c *gc.C) {
   334  	revno := 2
   335  	ms := s.AddMetaCharm(c, "mysql", metaBase, revno)
   336  	svc := s.AddTestingService(c, "fakemysql", ms)
   337  	cfg := state.SetCharmConfig{Charm: ms}
   338  	err := svc.SetCharm(cfg)
   339  	c.Assert(err, jc.ErrorIsNil)
   340  
   341  	for i, t := range setCharmEndpointsTests {
   342  		c.Logf("test %d: %s", i, t.summary)
   343  
   344  		newCh := s.AddMetaCharm(c, "mysql", t.meta, revno+i+1)
   345  		cfg := state.SetCharmConfig{Charm: newCh}
   346  		err = svc.SetCharm(cfg)
   347  		if t.err != "" {
   348  			c.Assert(err, gc.ErrorMatches, t.err)
   349  		} else {
   350  			c.Assert(err, jc.ErrorIsNil)
   351  		}
   352  	}
   353  
   354  	err = svc.Destroy()
   355  	c.Assert(err, jc.ErrorIsNil)
   356  }
   357  
   358  func (s *ServiceSuite) TestSetCharmChecksEndpointsWithRelations(c *gc.C) {
   359  	revno := 2
   360  	providerCharm := s.AddMetaCharm(c, "mysql", metaDifferentProvider, revno)
   361  	providerSvc := s.AddTestingService(c, "myprovider", providerCharm)
   362  
   363  	cfg := state.SetCharmConfig{Charm: providerCharm}
   364  	err := providerSvc.SetCharm(cfg)
   365  	c.Assert(err, jc.ErrorIsNil)
   366  
   367  	revno++
   368  	requirerCharm := s.AddMetaCharm(c, "mysql", metaDifferentRequirer, revno)
   369  	requirerSvc := s.AddTestingService(c, "myrequirer", requirerCharm)
   370  	cfg = state.SetCharmConfig{Charm: requirerCharm}
   371  	err = requirerSvc.SetCharm(cfg)
   372  	c.Assert(err, jc.ErrorIsNil)
   373  
   374  	eps, err := s.State.InferEndpoints("myprovider:kludge", "myrequirer:kludge")
   375  	c.Assert(err, jc.ErrorIsNil)
   376  	_, err = s.State.AddRelation(eps...)
   377  	c.Assert(err, jc.ErrorIsNil)
   378  
   379  	revno++
   380  	baseCharm := s.AddMetaCharm(c, "mysql", metaBase, revno)
   381  	cfg = state.SetCharmConfig{Charm: baseCharm}
   382  	err = providerSvc.SetCharm(cfg)
   383  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "myprovider" to charm "local:quantal/quantal-mysql-4": would break relation "myrequirer:kludge myprovider:kludge"`)
   384  	err = requirerSvc.SetCharm(cfg)
   385  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "myrequirer" to charm "local:quantal/quantal-mysql-4": would break relation "myrequirer:kludge myprovider:kludge"`)
   386  }
   387  
   388  var stringConfig = `
   389  options:
   390    key: {default: My Key, description: Desc, type: string}
   391  `
   392  var emptyConfig = `
   393  options: {}
   394  `
   395  var floatConfig = `
   396  options:
   397    key: {default: 0.42, description: Float key, type: float}
   398  `
   399  var newStringConfig = `
   400  options:
   401    key: {default: My Key, description: Desc, type: string}
   402    other: {default: None, description: My Other, type: string}
   403  `
   404  
   405  var setCharmConfigTests = []struct {
   406  	summary     string
   407  	startconfig string
   408  	startvalues charm.Settings
   409  	endconfig   string
   410  	endvalues   charm.Settings
   411  	err         string
   412  }{{
   413  	summary:     "add float key to empty config",
   414  	startconfig: emptyConfig,
   415  	endconfig:   floatConfig,
   416  }, {
   417  	summary:     "add string key to empty config",
   418  	startconfig: emptyConfig,
   419  	endconfig:   stringConfig,
   420  }, {
   421  	summary:     "add string key and preserve existing values",
   422  	startconfig: stringConfig,
   423  	startvalues: charm.Settings{"key": "foo"},
   424  	endconfig:   newStringConfig,
   425  	endvalues:   charm.Settings{"key": "foo"},
   426  }, {
   427  	summary:     "remove string key",
   428  	startconfig: stringConfig,
   429  	startvalues: charm.Settings{"key": "value"},
   430  	endconfig:   emptyConfig,
   431  }, {
   432  	summary:     "remove float key",
   433  	startconfig: floatConfig,
   434  	startvalues: charm.Settings{"key": 123.45},
   435  	endconfig:   emptyConfig,
   436  }, {
   437  	summary:     "change key type without values",
   438  	startconfig: stringConfig,
   439  	endconfig:   floatConfig,
   440  }, {
   441  	summary:     "change key type with values",
   442  	startconfig: stringConfig,
   443  	startvalues: charm.Settings{"key": "value"},
   444  	endconfig:   floatConfig,
   445  }}
   446  
   447  func (s *ServiceSuite) TestSetCharmConfig(c *gc.C) {
   448  	charms := map[string]*state.Charm{
   449  		stringConfig:    s.AddConfigCharm(c, "wordpress", stringConfig, 1),
   450  		emptyConfig:     s.AddConfigCharm(c, "wordpress", emptyConfig, 2),
   451  		floatConfig:     s.AddConfigCharm(c, "wordpress", floatConfig, 3),
   452  		newStringConfig: s.AddConfigCharm(c, "wordpress", newStringConfig, 4),
   453  	}
   454  
   455  	for i, t := range setCharmConfigTests {
   456  		c.Logf("test %d: %s", i, t.summary)
   457  
   458  		origCh := charms[t.startconfig]
   459  		svc := s.AddTestingService(c, "wordpress", origCh)
   460  		err := svc.UpdateConfigSettings(t.startvalues)
   461  		c.Assert(err, jc.ErrorIsNil)
   462  
   463  		newCh := charms[t.endconfig]
   464  		cfg := state.SetCharmConfig{Charm: newCh}
   465  		err = svc.SetCharm(cfg)
   466  		var expectVals charm.Settings
   467  		var expectCh *state.Charm
   468  		if t.err != "" {
   469  			c.Assert(err, gc.ErrorMatches, t.err)
   470  			expectCh = origCh
   471  			expectVals = t.startvalues
   472  		} else {
   473  			c.Assert(err, jc.ErrorIsNil)
   474  			expectCh = newCh
   475  			expectVals = t.endvalues
   476  		}
   477  
   478  		sch, _, err := svc.Charm()
   479  		c.Assert(err, jc.ErrorIsNil)
   480  		c.Assert(sch.URL(), gc.DeepEquals, expectCh.URL())
   481  		settings, err := svc.ConfigSettings()
   482  		c.Assert(err, jc.ErrorIsNil)
   483  		if len(expectVals) == 0 {
   484  			c.Assert(settings, gc.HasLen, 0)
   485  		} else {
   486  			c.Assert(settings, gc.DeepEquals, expectVals)
   487  		}
   488  
   489  		err = svc.Destroy()
   490  		c.Assert(err, jc.ErrorIsNil)
   491  	}
   492  }
   493  
   494  func (s *ServiceSuite) TestSetCharmWithDyingService(c *gc.C) {
   495  	sch := s.AddMetaCharm(c, "mysql", metaBase, 2)
   496  
   497  	_, err := s.mysql.AddUnit()
   498  	c.Assert(err, jc.ErrorIsNil)
   499  	err = s.mysql.Destroy()
   500  	c.Assert(err, jc.ErrorIsNil)
   501  	assertLife(c, s.mysql, state.Dying)
   502  	cfg := state.SetCharmConfig{
   503  		Charm:      sch,
   504  		ForceUnits: true,
   505  	}
   506  	err = s.mysql.SetCharm(cfg)
   507  	c.Assert(err, jc.ErrorIsNil)
   508  }
   509  
   510  func (s *ServiceSuite) TestSequenceUnitIdsAfterDestroy(c *gc.C) {
   511  	unit, err := s.mysql.AddUnit()
   512  	c.Assert(err, jc.ErrorIsNil)
   513  	c.Assert(unit.Name(), gc.Equals, "mysql/0")
   514  	err = unit.Destroy()
   515  	c.Assert(err, jc.ErrorIsNil)
   516  	err = s.mysql.Destroy()
   517  	c.Assert(err, jc.ErrorIsNil)
   518  	s.mysql = s.AddTestingService(c, "mysql", s.charm)
   519  	unit, err = s.mysql.AddUnit()
   520  	c.Assert(err, jc.ErrorIsNil)
   521  	c.Assert(unit.Name(), gc.Equals, "mysql/1")
   522  }
   523  
   524  func (s *ServiceSuite) TestSequenceUnitIds(c *gc.C) {
   525  	unit, err := s.mysql.AddUnit()
   526  	c.Assert(err, jc.ErrorIsNil)
   527  	c.Assert(unit.Name(), gc.Equals, "mysql/0")
   528  	unit, err = s.mysql.AddUnit()
   529  	c.Assert(err, jc.ErrorIsNil)
   530  	c.Assert(unit.Name(), gc.Equals, "mysql/1")
   531  }
   532  
   533  func (s *ServiceSuite) TestSetCharmWhenDead(c *gc.C) {
   534  	sch := s.AddMetaCharm(c, "mysql", metaBase, 2)
   535  
   536  	defer state.SetBeforeHooks(c, s.State, func() {
   537  		_, err := s.mysql.AddUnit()
   538  		err = s.mysql.Destroy()
   539  		c.Assert(err, jc.ErrorIsNil)
   540  		assertLife(c, s.mysql, state.Dying)
   541  
   542  		// Change the service life to Dead manually, as there's no
   543  		// direct way of doing that otherwise.
   544  		ops := []txn.Op{{
   545  			C:      state.ServicesC,
   546  			Id:     state.DocID(s.State, s.mysql.Name()),
   547  			Update: bson.D{{"$set", bson.D{{"life", state.Dead}}}},
   548  		}}
   549  
   550  		err = state.RunTransaction(s.State, ops)
   551  		c.Assert(err, jc.ErrorIsNil)
   552  		assertLife(c, s.mysql, state.Dead)
   553  	}).Check()
   554  
   555  	cfg := state.SetCharmConfig{
   556  		Charm:      sch,
   557  		ForceUnits: true,
   558  	}
   559  	err := s.mysql.SetCharm(cfg)
   560  	c.Assert(err, gc.Equals, state.ErrDead)
   561  }
   562  
   563  func (s *ServiceSuite) TestSetCharmWithRemovedService(c *gc.C) {
   564  	sch := s.AddMetaCharm(c, "mysql", metaBase, 2)
   565  
   566  	err := s.mysql.Destroy()
   567  	c.Assert(err, jc.ErrorIsNil)
   568  	assertRemoved(c, s.mysql)
   569  
   570  	cfg := state.SetCharmConfig{
   571  		Charm:      sch,
   572  		ForceUnits: true,
   573  	}
   574  
   575  	err = s.mysql.SetCharm(cfg)
   576  	c.Assert(err, gc.Equals, state.ErrDead)
   577  }
   578  
   579  func (s *ServiceSuite) TestSetCharmWhenRemoved(c *gc.C) {
   580  	sch := s.AddMetaCharm(c, "mysql", metaBase, 2)
   581  
   582  	defer state.SetBeforeHooks(c, s.State, func() {
   583  		err := s.mysql.Destroy()
   584  		c.Assert(err, jc.ErrorIsNil)
   585  		assertRemoved(c, s.mysql)
   586  	}).Check()
   587  
   588  	cfg := state.SetCharmConfig{
   589  		Charm:      sch,
   590  		ForceUnits: true,
   591  	}
   592  	err := s.mysql.SetCharm(cfg)
   593  	c.Assert(err, gc.Equals, state.ErrDead)
   594  }
   595  
   596  func (s *ServiceSuite) TestSetCharmWhenDyingIsOK(c *gc.C) {
   597  	sch := s.AddMetaCharm(c, "mysql", metaBase, 2)
   598  
   599  	defer state.SetBeforeHooks(c, s.State, func() {
   600  		_, err := s.mysql.AddUnit()
   601  		c.Assert(err, jc.ErrorIsNil)
   602  		err = s.mysql.Destroy()
   603  		c.Assert(err, jc.ErrorIsNil)
   604  		assertLife(c, s.mysql, state.Dying)
   605  	}).Check()
   606  
   607  	cfg := state.SetCharmConfig{
   608  		Charm:      sch,
   609  		ForceUnits: true,
   610  	}
   611  	err := s.mysql.SetCharm(cfg)
   612  	c.Assert(err, jc.ErrorIsNil)
   613  	assertLife(c, s.mysql, state.Dying)
   614  }
   615  
   616  func (s *ServiceSuite) TestSetCharmRetriesWithSameCharmURL(c *gc.C) {
   617  	sch := s.AddMetaCharm(c, "mysql", metaBase, 2)
   618  
   619  	defer state.SetTestHooks(c, s.State,
   620  		jujutxn.TestHook{
   621  			Before: func() {
   622  				currentCh, force, err := s.mysql.Charm()
   623  				c.Assert(err, jc.ErrorIsNil)
   624  				c.Assert(force, jc.IsFalse)
   625  				c.Assert(currentCh.URL(), jc.DeepEquals, s.charm.URL())
   626  
   627  				cfg := state.SetCharmConfig{Charm: sch}
   628  				err = s.mysql.SetCharm(cfg)
   629  				c.Assert(err, jc.ErrorIsNil)
   630  			},
   631  			After: func() {
   632  				// Verify the before hook worked.
   633  				currentCh, force, err := s.mysql.Charm()
   634  				c.Assert(err, jc.ErrorIsNil)
   635  				c.Assert(force, jc.IsFalse)
   636  				c.Assert(currentCh.URL(), jc.DeepEquals, sch.URL())
   637  			},
   638  		},
   639  		jujutxn.TestHook{
   640  			Before: nil, // Ensure there will be a retry.
   641  			After: func() {
   642  				// Verify it worked after the retry.
   643  				err := s.mysql.Refresh()
   644  				c.Assert(err, jc.ErrorIsNil)
   645  				currentCh, force, err := s.mysql.Charm()
   646  				c.Assert(err, jc.ErrorIsNil)
   647  				c.Assert(force, jc.IsTrue)
   648  				c.Assert(currentCh.URL(), jc.DeepEquals, sch.URL())
   649  			},
   650  		},
   651  	).Check()
   652  
   653  	cfg := state.SetCharmConfig{
   654  		Charm:      sch,
   655  		ForceUnits: true,
   656  	}
   657  	err := s.mysql.SetCharm(cfg)
   658  	c.Assert(err, jc.ErrorIsNil)
   659  }
   660  
   661  func (s *ServiceSuite) TestSetCharmRetriesWhenOldSettingsChanged(c *gc.C) {
   662  	revno := 2 // revno 1 is used by SetUpSuite
   663  	oldCh := s.AddConfigCharm(c, "mysql", stringConfig, revno)
   664  	newCh := s.AddConfigCharm(c, "mysql", stringConfig, revno+1)
   665  	cfg := state.SetCharmConfig{Charm: oldCh}
   666  	err := s.mysql.SetCharm(cfg)
   667  	c.Assert(err, jc.ErrorIsNil)
   668  
   669  	defer state.SetBeforeHooks(c, s.State,
   670  		func() {
   671  			err := s.mysql.UpdateConfigSettings(charm.Settings{"key": "value"})
   672  			c.Assert(err, jc.ErrorIsNil)
   673  		},
   674  		nil, // Ensure there will be a retry.
   675  	).Check()
   676  
   677  	cfg = state.SetCharmConfig{
   678  		Charm:      newCh,
   679  		ForceUnits: true,
   680  	}
   681  	err = s.mysql.SetCharm(cfg)
   682  	c.Assert(err, jc.ErrorIsNil)
   683  }
   684  
   685  func (s *ServiceSuite) TestSetCharmRetriesWhenBothOldAndNewSettingsChanged(c *gc.C) {
   686  	revno := 2 // revno 1 is used by SetUpSuite
   687  	oldCh := s.AddConfigCharm(c, "mysql", stringConfig, revno)
   688  	newCh := s.AddConfigCharm(c, "mysql", stringConfig, revno+1)
   689  
   690  	defer state.SetTestHooks(c, s.State,
   691  		jujutxn.TestHook{
   692  			Before: func() {
   693  				// Add two units, which will keep the refcount of oldCh
   694  				// and newCh settings greater than 0, while the service's
   695  				// charm URLs change between oldCh and newCh. Ensure
   696  				// refcounts change as expected.
   697  				unit1, err := s.mysql.AddUnit()
   698  				c.Assert(err, jc.ErrorIsNil)
   699  				unit2, err := s.mysql.AddUnit()
   700  				c.Assert(err, jc.ErrorIsNil)
   701  				cfg := state.SetCharmConfig{Charm: newCh}
   702  				err = s.mysql.SetCharm(cfg)
   703  				c.Assert(err, jc.ErrorIsNil)
   704  				assertSettingsRef(c, s.State, "mysql", newCh, 1)
   705  				assertNoSettingsRef(c, s.State, "mysql", oldCh)
   706  				err = unit1.SetCharmURL(newCh.URL())
   707  				c.Assert(err, jc.ErrorIsNil)
   708  				assertSettingsRef(c, s.State, "mysql", newCh, 2)
   709  				assertNoSettingsRef(c, s.State, "mysql", oldCh)
   710  				// Update newCh settings, switch to oldCh and update its
   711  				// settings as well.
   712  				err = s.mysql.UpdateConfigSettings(charm.Settings{"key": "value1"})
   713  				c.Assert(err, jc.ErrorIsNil)
   714  				cfg = state.SetCharmConfig{Charm: oldCh}
   715  
   716  				err = s.mysql.SetCharm(cfg)
   717  				c.Assert(err, jc.ErrorIsNil)
   718  				assertSettingsRef(c, s.State, "mysql", newCh, 1)
   719  				assertSettingsRef(c, s.State, "mysql", oldCh, 1)
   720  				err = unit2.SetCharmURL(oldCh.URL())
   721  				c.Assert(err, jc.ErrorIsNil)
   722  				assertSettingsRef(c, s.State, "mysql", newCh, 1)
   723  				assertSettingsRef(c, s.State, "mysql", oldCh, 2)
   724  				err = s.mysql.UpdateConfigSettings(charm.Settings{"key": "value2"})
   725  				c.Assert(err, jc.ErrorIsNil)
   726  			},
   727  			After: func() {
   728  				// Verify the charm and refcounts after the second attempt.
   729  				err := s.mysql.Refresh()
   730  				c.Assert(err, jc.ErrorIsNil)
   731  				currentCh, force, err := s.mysql.Charm()
   732  				c.Assert(err, jc.ErrorIsNil)
   733  				c.Assert(force, jc.IsFalse)
   734  				c.Assert(currentCh.URL(), jc.DeepEquals, oldCh.URL())
   735  				assertSettingsRef(c, s.State, "mysql", newCh, 1)
   736  				assertSettingsRef(c, s.State, "mysql", oldCh, 2)
   737  			},
   738  		},
   739  		jujutxn.TestHook{
   740  			Before: func() {
   741  				// SetCharm has refreshed its cached settings for oldCh
   742  				// and newCh. Change them again to trigger another
   743  				// attempt.
   744  				cfg := state.SetCharmConfig{Charm: newCh}
   745  
   746  				err := s.mysql.SetCharm(cfg)
   747  				c.Assert(err, jc.ErrorIsNil)
   748  				assertSettingsRef(c, s.State, "mysql", newCh, 2)
   749  				assertSettingsRef(c, s.State, "mysql", oldCh, 1)
   750  				err = s.mysql.UpdateConfigSettings(charm.Settings{"key": "value3"})
   751  				c.Assert(err, jc.ErrorIsNil)
   752  
   753  				cfg = state.SetCharmConfig{Charm: oldCh}
   754  				err = s.mysql.SetCharm(cfg)
   755  				c.Assert(err, jc.ErrorIsNil)
   756  				assertSettingsRef(c, s.State, "mysql", newCh, 1)
   757  				assertSettingsRef(c, s.State, "mysql", oldCh, 2)
   758  				err = s.mysql.UpdateConfigSettings(charm.Settings{"key": "value4"})
   759  				c.Assert(err, jc.ErrorIsNil)
   760  			},
   761  			After: func() {
   762  				// Verify the charm and refcounts after the third attempt.
   763  				err := s.mysql.Refresh()
   764  				c.Assert(err, jc.ErrorIsNil)
   765  				currentCh, force, err := s.mysql.Charm()
   766  				c.Assert(err, jc.ErrorIsNil)
   767  				c.Assert(force, jc.IsFalse)
   768  				c.Assert(currentCh.URL(), jc.DeepEquals, oldCh.URL())
   769  				assertSettingsRef(c, s.State, "mysql", newCh, 1)
   770  				assertSettingsRef(c, s.State, "mysql", oldCh, 2)
   771  			},
   772  		},
   773  		jujutxn.TestHook{
   774  			Before: nil, // Ensure there will be a (final) retry.
   775  			After: func() {
   776  				// Verify the charm and refcounts after the final third attempt.
   777  				err := s.mysql.Refresh()
   778  				c.Assert(err, jc.ErrorIsNil)
   779  				currentCh, force, err := s.mysql.Charm()
   780  				c.Assert(err, jc.ErrorIsNil)
   781  				c.Assert(force, jc.IsTrue)
   782  				c.Assert(currentCh.URL(), jc.DeepEquals, newCh.URL())
   783  				assertSettingsRef(c, s.State, "mysql", newCh, 2)
   784  				assertSettingsRef(c, s.State, "mysql", oldCh, 1)
   785  			},
   786  		},
   787  	).Check()
   788  
   789  	cfg := state.SetCharmConfig{
   790  		Charm:      newCh,
   791  		ForceUnits: true,
   792  	}
   793  	err := s.mysql.SetCharm(cfg)
   794  	c.Assert(err, jc.ErrorIsNil)
   795  }
   796  
   797  func (s *ServiceSuite) TestSetCharmRetriesWhenOldBindingsChanged(c *gc.C) {
   798  	revno := 2 // revno 1 is used by SetUpSuite
   799  	mysqlKey := state.ServiceGlobalKey(s.mysql.Name())
   800  	oldCharm := s.AddMetaCharm(c, "mysql", metaDifferentRequirer, revno)
   801  	newCharm := s.AddMetaCharm(c, "mysql", metaExtraEndpoints, revno+1)
   802  
   803  	cfg := state.SetCharmConfig{Charm: oldCharm}
   804  	err := s.mysql.SetCharm(cfg)
   805  	c.Assert(err, jc.ErrorIsNil)
   806  
   807  	oldBindings, err := s.mysql.EndpointBindings()
   808  	c.Assert(err, jc.ErrorIsNil)
   809  	c.Assert(oldBindings, jc.DeepEquals, map[string]string{
   810  		"server":  "",
   811  		"kludge":  "",
   812  		"cluster": "",
   813  	})
   814  	_, err = s.State.AddSpace("db", "", nil, true)
   815  	c.Assert(err, jc.ErrorIsNil)
   816  	_, err = s.State.AddSpace("admin", "", nil, false)
   817  	c.Assert(err, jc.ErrorIsNil)
   818  
   819  	updateBindings := func(updatesMap bson.M) {
   820  		ops := []txn.Op{{
   821  			C:      state.EndpointBindingsC,
   822  			Id:     mysqlKey,
   823  			Update: bson.D{{"$set", updatesMap}},
   824  		}}
   825  		err := state.RunTransaction(s.State, ops)
   826  		c.Assert(err, jc.ErrorIsNil)
   827  	}
   828  
   829  	defer state.SetTestHooks(c, s.State,
   830  		jujutxn.TestHook{
   831  			Before: func() {
   832  				// First change.
   833  				updateBindings(bson.M{
   834  					"bindings.server": "db",
   835  					"bindings.kludge": "admin", // will be removed before newCharm is set.
   836  				})
   837  			},
   838  			After: func() {
   839  				// Second change.
   840  				updateBindings(bson.M{
   841  					"bindings.cluster": "admin",
   842  				})
   843  			},
   844  		},
   845  		jujutxn.TestHook{
   846  			Before: nil, // Ensure there will be a (final) retry.
   847  			After: func() {
   848  				// Verify final bindings.
   849  				newBindings, err := s.mysql.EndpointBindings()
   850  				c.Assert(err, jc.ErrorIsNil)
   851  				c.Assert(newBindings, jc.DeepEquals, map[string]string{
   852  					"server":  "db", // from the first change.
   853  					"foo":     "",
   854  					"client":  "",
   855  					"baz":     "",
   856  					"cluster": "admin", // from the second change.
   857  					"just":    "",
   858  				})
   859  			},
   860  		},
   861  	).Check()
   862  
   863  	cfg = state.SetCharmConfig{
   864  		Charm:      newCharm,
   865  		ForceUnits: true,
   866  	}
   867  	err = s.mysql.SetCharm(cfg)
   868  	c.Assert(err, jc.ErrorIsNil)
   869  }
   870  
   871  var serviceUpdateConfigSettingsTests = []struct {
   872  	about   string
   873  	initial charm.Settings
   874  	update  charm.Settings
   875  	expect  charm.Settings
   876  	err     string
   877  }{{
   878  	about:  "unknown option",
   879  	update: charm.Settings{"foo": "bar"},
   880  	err:    `unknown option "foo"`,
   881  }, {
   882  	about:  "bad type",
   883  	update: charm.Settings{"skill-level": "profound"},
   884  	err:    `option "skill-level" expected int, got "profound"`,
   885  }, {
   886  	about:  "set string",
   887  	update: charm.Settings{"outlook": "positive"},
   888  	expect: charm.Settings{"outlook": "positive"},
   889  }, {
   890  	about:   "unset string and set another",
   891  	initial: charm.Settings{"outlook": "positive"},
   892  	update:  charm.Settings{"outlook": nil, "title": "sir"},
   893  	expect:  charm.Settings{"title": "sir"},
   894  }, {
   895  	about:  "unset missing string",
   896  	update: charm.Settings{"outlook": nil},
   897  }, {
   898  	about:   `empty strings are valid`,
   899  	initial: charm.Settings{"outlook": "positive"},
   900  	update:  charm.Settings{"outlook": "", "title": ""},
   901  	expect:  charm.Settings{"outlook": "", "title": ""},
   902  }, {
   903  	about:   "preserve existing value",
   904  	initial: charm.Settings{"title": "sir"},
   905  	update:  charm.Settings{"username": "admin001"},
   906  	expect:  charm.Settings{"username": "admin001", "title": "sir"},
   907  }, {
   908  	about:   "unset a default value, set a different default",
   909  	initial: charm.Settings{"username": "admin001", "title": "sir"},
   910  	update:  charm.Settings{"username": nil, "title": "My Title"},
   911  	expect:  charm.Settings{"title": "My Title"},
   912  }, {
   913  	about:  "non-string type",
   914  	update: charm.Settings{"skill-level": 303},
   915  	expect: charm.Settings{"skill-level": int64(303)},
   916  }, {
   917  	about:   "unset non-string type",
   918  	initial: charm.Settings{"skill-level": 303},
   919  	update:  charm.Settings{"skill-level": nil},
   920  }}
   921  
   922  func (s *ServiceSuite) TestUpdateConfigSettings(c *gc.C) {
   923  	sch := s.AddTestingCharm(c, "dummy")
   924  	for i, t := range serviceUpdateConfigSettingsTests {
   925  		c.Logf("test %d. %s", i, t.about)
   926  		svc := s.AddTestingService(c, "dummy-service", sch)
   927  		if t.initial != nil {
   928  			err := svc.UpdateConfigSettings(t.initial)
   929  			c.Assert(err, jc.ErrorIsNil)
   930  		}
   931  		err := svc.UpdateConfigSettings(t.update)
   932  		if t.err != "" {
   933  			c.Assert(err, gc.ErrorMatches, t.err)
   934  		} else {
   935  			c.Assert(err, jc.ErrorIsNil)
   936  			settings, err := svc.ConfigSettings()
   937  			c.Assert(err, jc.ErrorIsNil)
   938  			expect := t.expect
   939  			if expect == nil {
   940  				expect = charm.Settings{}
   941  			}
   942  			c.Assert(settings, gc.DeepEquals, expect)
   943  		}
   944  		err = svc.Destroy()
   945  		c.Assert(err, jc.ErrorIsNil)
   946  	}
   947  }
   948  
   949  func assertNoSettingsRef(c *gc.C, st *state.State, svcName string, sch *state.Charm) {
   950  	_, err := state.ServiceSettingsRefCount(st, svcName, sch.URL())
   951  	c.Assert(err, gc.Equals, mgo.ErrNotFound)
   952  }
   953  
   954  func assertSettingsRef(c *gc.C, st *state.State, svcName string, sch *state.Charm, refcount int) {
   955  	rc, err := state.ServiceSettingsRefCount(st, svcName, sch.URL())
   956  	c.Assert(err, jc.ErrorIsNil)
   957  	c.Assert(rc, gc.Equals, refcount)
   958  }
   959  
   960  func (s *ServiceSuite) TestSettingsRefCountWorks(c *gc.C) {
   961  	// This test ensures the service settings per charm URL are
   962  	// properly reference counted.
   963  	oldCh := s.AddConfigCharm(c, "wordpress", emptyConfig, 1)
   964  	newCh := s.AddConfigCharm(c, "wordpress", emptyConfig, 2)
   965  	svcName := "mywp"
   966  
   967  	// Both refcounts are zero initially.
   968  	assertNoSettingsRef(c, s.State, svcName, oldCh)
   969  	assertNoSettingsRef(c, s.State, svcName, newCh)
   970  
   971  	// svc is using oldCh, so its settings refcount is incremented.
   972  	svc := s.AddTestingService(c, svcName, oldCh)
   973  	assertSettingsRef(c, s.State, svcName, oldCh, 1)
   974  	assertNoSettingsRef(c, s.State, svcName, newCh)
   975  
   976  	// Changing to the same charm does not change the refcount.
   977  	cfg := state.SetCharmConfig{Charm: oldCh}
   978  	err := svc.SetCharm(cfg)
   979  	c.Assert(err, jc.ErrorIsNil)
   980  	assertSettingsRef(c, s.State, svcName, oldCh, 1)
   981  	assertNoSettingsRef(c, s.State, svcName, newCh)
   982  
   983  	// Changing from oldCh to newCh causes the refcount of oldCh's
   984  	// settings to be decremented, while newCh's settings is
   985  	// incremented. Consequently, because oldCh's refcount is 0, the
   986  	// settings doc will be removed.
   987  	cfg = state.SetCharmConfig{Charm: newCh}
   988  	err = svc.SetCharm(cfg)
   989  	c.Assert(err, jc.ErrorIsNil)
   990  	assertNoSettingsRef(c, s.State, svcName, oldCh)
   991  	assertSettingsRef(c, s.State, svcName, newCh, 1)
   992  
   993  	// The same but newCh swapped with oldCh.
   994  	cfg = state.SetCharmConfig{Charm: oldCh}
   995  	err = svc.SetCharm(cfg)
   996  	c.Assert(err, jc.ErrorIsNil)
   997  	assertSettingsRef(c, s.State, svcName, oldCh, 1)
   998  	assertNoSettingsRef(c, s.State, svcName, newCh)
   999  
  1000  	// Adding a unit without a charm URL set does not affect the
  1001  	// refcount.
  1002  	u, err := svc.AddUnit()
  1003  	c.Assert(err, jc.ErrorIsNil)
  1004  	curl, ok := u.CharmURL()
  1005  	c.Assert(ok, jc.IsFalse)
  1006  	assertSettingsRef(c, s.State, svcName, oldCh, 1)
  1007  	assertNoSettingsRef(c, s.State, svcName, newCh)
  1008  
  1009  	// Setting oldCh as the units charm URL increments oldCh, which is
  1010  	// used by svc as well, hence 2.
  1011  	err = u.SetCharmURL(oldCh.URL())
  1012  	c.Assert(err, jc.ErrorIsNil)
  1013  	curl, ok = u.CharmURL()
  1014  	c.Assert(ok, jc.IsTrue)
  1015  	c.Assert(curl, gc.DeepEquals, oldCh.URL())
  1016  	assertSettingsRef(c, s.State, svcName, oldCh, 2)
  1017  	assertNoSettingsRef(c, s.State, svcName, newCh)
  1018  
  1019  	// A dead unit does not decrement the refcount.
  1020  	err = u.EnsureDead()
  1021  	c.Assert(err, jc.ErrorIsNil)
  1022  	assertSettingsRef(c, s.State, svcName, oldCh, 2)
  1023  	assertNoSettingsRef(c, s.State, svcName, newCh)
  1024  
  1025  	// Once the unit is removed, refcount is decremented.
  1026  	err = u.Remove()
  1027  	c.Assert(err, jc.ErrorIsNil)
  1028  	assertSettingsRef(c, s.State, svcName, oldCh, 1)
  1029  	assertNoSettingsRef(c, s.State, svcName, newCh)
  1030  
  1031  	// Finally, after the service is destroyed and removed (since the
  1032  	// last unit's gone), the refcount is again decremented.
  1033  	err = svc.Destroy()
  1034  	c.Assert(err, jc.ErrorIsNil)
  1035  	assertNoSettingsRef(c, s.State, svcName, oldCh)
  1036  	assertNoSettingsRef(c, s.State, svcName, newCh)
  1037  }
  1038  
  1039  const mysqlBaseMeta = `
  1040  name: mysql
  1041  summary: "Database engine"
  1042  description: "A pretty popular database"
  1043  provides:
  1044    server: mysql
  1045  `
  1046  const onePeerMeta = `
  1047  peers:
  1048    cluster: mysql
  1049  `
  1050  const twoPeersMeta = `
  1051  peers:
  1052    cluster: mysql
  1053    loadbalancer: phony
  1054  `
  1055  
  1056  func (s *ServiceSuite) assertServiceRelations(c *gc.C, svc *state.Service, expectedKeys ...string) []*state.Relation {
  1057  	rels, err := svc.Relations()
  1058  	c.Assert(err, jc.ErrorIsNil)
  1059  	if len(rels) == 0 {
  1060  		return nil
  1061  	}
  1062  	relKeys := make([]string, len(expectedKeys))
  1063  	for i, rel := range rels {
  1064  		relKeys[i] = rel.String()
  1065  	}
  1066  	sort.Strings(relKeys)
  1067  	c.Assert(relKeys, gc.DeepEquals, expectedKeys)
  1068  	return rels
  1069  }
  1070  
  1071  func (s *ServiceSuite) TestNewPeerRelationsAddedOnUpgrade(c *gc.C) {
  1072  	// Original mysql charm has no peer relations.
  1073  	oldCh := s.AddMetaCharm(c, "mysql", mysqlBaseMeta+onePeerMeta, 2)
  1074  	newCh := s.AddMetaCharm(c, "mysql", mysqlBaseMeta+twoPeersMeta, 3)
  1075  
  1076  	// No relations joined yet.
  1077  	s.assertServiceRelations(c, s.mysql)
  1078  
  1079  	cfg := state.SetCharmConfig{Charm: oldCh}
  1080  	err := s.mysql.SetCharm(cfg)
  1081  	c.Assert(err, jc.ErrorIsNil)
  1082  	s.assertServiceRelations(c, s.mysql, "mysql:cluster")
  1083  
  1084  	cfg = state.SetCharmConfig{Charm: newCh}
  1085  	err = s.mysql.SetCharm(cfg)
  1086  	c.Assert(err, jc.ErrorIsNil)
  1087  	rels := s.assertServiceRelations(c, s.mysql, "mysql:cluster", "mysql:loadbalancer")
  1088  
  1089  	// Check state consistency by attempting to destroy the service.
  1090  	err = s.mysql.Destroy()
  1091  	c.Assert(err, jc.ErrorIsNil)
  1092  
  1093  	// Check the peer relations got destroyed as well.
  1094  	for _, rel := range rels {
  1095  		err = rel.Refresh()
  1096  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1097  	}
  1098  }
  1099  
  1100  func jujuInfoEp(serviceName string) state.Endpoint {
  1101  	return state.Endpoint{
  1102  		ServiceName: serviceName,
  1103  		Relation: charm.Relation{
  1104  			Interface: "juju-info",
  1105  			Name:      "juju-info",
  1106  			Role:      charm.RoleProvider,
  1107  			Scope:     charm.ScopeGlobal,
  1108  		},
  1109  	}
  1110  }
  1111  
  1112  func (s *ServiceSuite) TestTag(c *gc.C) {
  1113  	c.Assert(s.mysql.Tag().String(), gc.Equals, "service-mysql")
  1114  }
  1115  
  1116  func (s *ServiceSuite) TestMysqlEndpoints(c *gc.C) {
  1117  	_, err := s.mysql.Endpoint("mysql")
  1118  	c.Assert(err, gc.ErrorMatches, `service "mysql" has no "mysql" relation`)
  1119  
  1120  	jiEP, err := s.mysql.Endpoint("juju-info")
  1121  	c.Assert(err, jc.ErrorIsNil)
  1122  	c.Assert(jiEP, gc.DeepEquals, jujuInfoEp("mysql"))
  1123  
  1124  	serverEP, err := s.mysql.Endpoint("server")
  1125  	c.Assert(err, jc.ErrorIsNil)
  1126  	c.Assert(serverEP, gc.DeepEquals, state.Endpoint{
  1127  		ServiceName: "mysql",
  1128  		Relation: charm.Relation{
  1129  			Interface: "mysql",
  1130  			Name:      "server",
  1131  			Role:      charm.RoleProvider,
  1132  			Scope:     charm.ScopeGlobal,
  1133  		},
  1134  	})
  1135  
  1136  	eps, err := s.mysql.Endpoints()
  1137  	c.Assert(err, jc.ErrorIsNil)
  1138  	c.Assert(eps, gc.DeepEquals, []state.Endpoint{jiEP, serverEP})
  1139  }
  1140  
  1141  func (s *ServiceSuite) TestRiakEndpoints(c *gc.C) {
  1142  	riak := s.AddTestingService(c, "myriak", s.AddTestingCharm(c, "riak"))
  1143  
  1144  	_, err := riak.Endpoint("garble")
  1145  	c.Assert(err, gc.ErrorMatches, `service "myriak" has no "garble" relation`)
  1146  
  1147  	jiEP, err := riak.Endpoint("juju-info")
  1148  	c.Assert(err, jc.ErrorIsNil)
  1149  	c.Assert(jiEP, gc.DeepEquals, jujuInfoEp("myriak"))
  1150  
  1151  	ringEP, err := riak.Endpoint("ring")
  1152  	c.Assert(err, jc.ErrorIsNil)
  1153  	c.Assert(ringEP, gc.DeepEquals, state.Endpoint{
  1154  		ServiceName: "myriak",
  1155  		Relation: charm.Relation{
  1156  			Interface: "riak",
  1157  			Name:      "ring",
  1158  			Role:      charm.RolePeer,
  1159  			Scope:     charm.ScopeGlobal,
  1160  			Limit:     1,
  1161  		},
  1162  	})
  1163  
  1164  	adminEP, err := riak.Endpoint("admin")
  1165  	c.Assert(err, jc.ErrorIsNil)
  1166  	c.Assert(adminEP, gc.DeepEquals, state.Endpoint{
  1167  		ServiceName: "myriak",
  1168  		Relation: charm.Relation{
  1169  			Interface: "http",
  1170  			Name:      "admin",
  1171  			Role:      charm.RoleProvider,
  1172  			Scope:     charm.ScopeGlobal,
  1173  		},
  1174  	})
  1175  
  1176  	endpointEP, err := riak.Endpoint("endpoint")
  1177  	c.Assert(err, jc.ErrorIsNil)
  1178  	c.Assert(endpointEP, gc.DeepEquals, state.Endpoint{
  1179  		ServiceName: "myriak",
  1180  		Relation: charm.Relation{
  1181  			Interface: "http",
  1182  			Name:      "endpoint",
  1183  			Role:      charm.RoleProvider,
  1184  			Scope:     charm.ScopeGlobal,
  1185  		},
  1186  	})
  1187  
  1188  	eps, err := riak.Endpoints()
  1189  	c.Assert(err, jc.ErrorIsNil)
  1190  	c.Assert(eps, gc.DeepEquals, []state.Endpoint{adminEP, endpointEP, jiEP, ringEP})
  1191  }
  1192  
  1193  func (s *ServiceSuite) TestWordpressEndpoints(c *gc.C) {
  1194  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  1195  
  1196  	_, err := wordpress.Endpoint("nonsense")
  1197  	c.Assert(err, gc.ErrorMatches, `service "wordpress" has no "nonsense" relation`)
  1198  
  1199  	jiEP, err := wordpress.Endpoint("juju-info")
  1200  	c.Assert(err, jc.ErrorIsNil)
  1201  	c.Assert(jiEP, gc.DeepEquals, jujuInfoEp("wordpress"))
  1202  
  1203  	urlEP, err := wordpress.Endpoint("url")
  1204  	c.Assert(err, jc.ErrorIsNil)
  1205  	c.Assert(urlEP, gc.DeepEquals, state.Endpoint{
  1206  		ServiceName: "wordpress",
  1207  		Relation: charm.Relation{
  1208  			Interface: "http",
  1209  			Name:      "url",
  1210  			Role:      charm.RoleProvider,
  1211  			Scope:     charm.ScopeGlobal,
  1212  		},
  1213  	})
  1214  
  1215  	ldEP, err := wordpress.Endpoint("logging-dir")
  1216  	c.Assert(err, jc.ErrorIsNil)
  1217  	c.Assert(ldEP, gc.DeepEquals, state.Endpoint{
  1218  		ServiceName: "wordpress",
  1219  		Relation: charm.Relation{
  1220  			Interface: "logging",
  1221  			Name:      "logging-dir",
  1222  			Role:      charm.RoleProvider,
  1223  			Scope:     charm.ScopeContainer,
  1224  		},
  1225  	})
  1226  
  1227  	mpEP, err := wordpress.Endpoint("monitoring-port")
  1228  	c.Assert(err, jc.ErrorIsNil)
  1229  	c.Assert(mpEP, gc.DeepEquals, state.Endpoint{
  1230  		ServiceName: "wordpress",
  1231  		Relation: charm.Relation{
  1232  			Interface: "monitoring",
  1233  			Name:      "monitoring-port",
  1234  			Role:      charm.RoleProvider,
  1235  			Scope:     charm.ScopeContainer,
  1236  		},
  1237  	})
  1238  
  1239  	dbEP, err := wordpress.Endpoint("db")
  1240  	c.Assert(err, jc.ErrorIsNil)
  1241  	c.Assert(dbEP, gc.DeepEquals, state.Endpoint{
  1242  		ServiceName: "wordpress",
  1243  		Relation: charm.Relation{
  1244  			Interface: "mysql",
  1245  			Name:      "db",
  1246  			Role:      charm.RoleRequirer,
  1247  			Scope:     charm.ScopeGlobal,
  1248  			Limit:     1,
  1249  		},
  1250  	})
  1251  
  1252  	cacheEP, err := wordpress.Endpoint("cache")
  1253  	c.Assert(err, jc.ErrorIsNil)
  1254  	c.Assert(cacheEP, gc.DeepEquals, state.Endpoint{
  1255  		ServiceName: "wordpress",
  1256  		Relation: charm.Relation{
  1257  			Interface: "varnish",
  1258  			Name:      "cache",
  1259  			Role:      charm.RoleRequirer,
  1260  			Scope:     charm.ScopeGlobal,
  1261  			Limit:     2,
  1262  			Optional:  true,
  1263  		},
  1264  	})
  1265  
  1266  	eps, err := wordpress.Endpoints()
  1267  	c.Assert(err, jc.ErrorIsNil)
  1268  	c.Assert(eps, gc.DeepEquals, []state.Endpoint{cacheEP, dbEP, jiEP, ldEP, mpEP, urlEP})
  1269  }
  1270  
  1271  func (s *ServiceSuite) TestServiceRefresh(c *gc.C) {
  1272  	s1, err := s.State.Service(s.mysql.Name())
  1273  	c.Assert(err, jc.ErrorIsNil)
  1274  
  1275  	cfg := state.SetCharmConfig{
  1276  		Charm:      s.charm,
  1277  		ForceUnits: true,
  1278  	}
  1279  
  1280  	err = s.mysql.SetCharm(cfg)
  1281  	c.Assert(err, jc.ErrorIsNil)
  1282  
  1283  	testch, force, err := s1.Charm()
  1284  	c.Assert(err, jc.ErrorIsNil)
  1285  	c.Assert(force, jc.IsFalse)
  1286  	c.Assert(testch.URL(), gc.DeepEquals, s.charm.URL())
  1287  
  1288  	err = s1.Refresh()
  1289  	c.Assert(err, jc.ErrorIsNil)
  1290  	testch, force, err = s1.Charm()
  1291  	c.Assert(err, jc.ErrorIsNil)
  1292  	c.Assert(force, jc.IsTrue)
  1293  	c.Assert(testch.URL(), gc.DeepEquals, s.charm.URL())
  1294  
  1295  	err = s.mysql.Destroy()
  1296  	c.Assert(err, jc.ErrorIsNil)
  1297  	err = s.mysql.Refresh()
  1298  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1299  }
  1300  
  1301  func (s *ServiceSuite) TestServiceExposed(c *gc.C) {
  1302  	// Check that querying for the exposed flag works correctly.
  1303  	c.Assert(s.mysql.IsExposed(), jc.IsFalse)
  1304  
  1305  	// Check that setting and clearing the exposed flag works correctly.
  1306  	err := s.mysql.SetExposed()
  1307  	c.Assert(err, jc.ErrorIsNil)
  1308  	c.Assert(s.mysql.IsExposed(), jc.IsTrue)
  1309  	err = s.mysql.ClearExposed()
  1310  	c.Assert(err, jc.ErrorIsNil)
  1311  	c.Assert(s.mysql.IsExposed(), jc.IsFalse)
  1312  
  1313  	// Check that setting and clearing the exposed flag repeatedly does not fail.
  1314  	err = s.mysql.SetExposed()
  1315  	c.Assert(err, jc.ErrorIsNil)
  1316  	err = s.mysql.SetExposed()
  1317  	c.Assert(err, jc.ErrorIsNil)
  1318  	err = s.mysql.ClearExposed()
  1319  	c.Assert(err, jc.ErrorIsNil)
  1320  	err = s.mysql.ClearExposed()
  1321  	c.Assert(err, jc.ErrorIsNil)
  1322  	err = s.mysql.SetExposed()
  1323  	c.Assert(err, jc.ErrorIsNil)
  1324  	c.Assert(s.mysql.IsExposed(), jc.IsTrue)
  1325  
  1326  	// Make the service Dying and check that ClearExposed and SetExposed fail.
  1327  	// TODO(fwereade): maybe service destruction should always unexpose?
  1328  	u, err := s.mysql.AddUnit()
  1329  	c.Assert(err, jc.ErrorIsNil)
  1330  	err = s.mysql.Destroy()
  1331  	c.Assert(err, jc.ErrorIsNil)
  1332  	err = s.mysql.ClearExposed()
  1333  	c.Assert(err, gc.ErrorMatches, notAliveErr)
  1334  	err = s.mysql.SetExposed()
  1335  	c.Assert(err, gc.ErrorMatches, notAliveErr)
  1336  
  1337  	// Remove the service and check that both fail.
  1338  	err = u.EnsureDead()
  1339  	c.Assert(err, jc.ErrorIsNil)
  1340  	err = u.Remove()
  1341  	c.Assert(err, jc.ErrorIsNil)
  1342  	err = s.mysql.SetExposed()
  1343  	c.Assert(err, gc.ErrorMatches, notAliveErr)
  1344  	err = s.mysql.ClearExposed()
  1345  	c.Assert(err, gc.ErrorMatches, notAliveErr)
  1346  }
  1347  
  1348  func (s *ServiceSuite) TestAddUnit(c *gc.C) {
  1349  	// Check that principal units can be added on their own.
  1350  	unitZero, err := s.mysql.AddUnit()
  1351  	c.Assert(err, jc.ErrorIsNil)
  1352  	c.Assert(unitZero.Name(), gc.Equals, "mysql/0")
  1353  	c.Assert(unitZero.IsPrincipal(), jc.IsTrue)
  1354  	c.Assert(unitZero.SubordinateNames(), gc.HasLen, 0)
  1355  	c.Assert(state.GetUnitModelUUID(unitZero), gc.Equals, s.State.ModelUUID())
  1356  
  1357  	unitOne, err := s.mysql.AddUnit()
  1358  	c.Assert(err, jc.ErrorIsNil)
  1359  	c.Assert(unitOne.Name(), gc.Equals, "mysql/1")
  1360  	c.Assert(unitOne.IsPrincipal(), jc.IsTrue)
  1361  	c.Assert(unitOne.SubordinateNames(), gc.HasLen, 0)
  1362  
  1363  	// Assign the principal unit to a machine.
  1364  	m, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1365  	c.Assert(err, jc.ErrorIsNil)
  1366  	err = unitZero.AssignToMachine(m)
  1367  	c.Assert(err, jc.ErrorIsNil)
  1368  
  1369  	// Add a subordinate service and check that units cannot be added directly.
  1370  	// to add a subordinate unit.
  1371  	subCharm := s.AddTestingCharm(c, "logging")
  1372  	logging := s.AddTestingService(c, "logging", subCharm)
  1373  	_, err = logging.AddUnit()
  1374  	c.Assert(err, gc.ErrorMatches, `cannot add unit to service "logging": service is a subordinate`)
  1375  
  1376  	// Indirectly create a subordinate unit by adding a relation and entering
  1377  	// scope as a principal.
  1378  	eps, err := s.State.InferEndpoints("logging", "mysql")
  1379  	c.Assert(err, jc.ErrorIsNil)
  1380  	rel, err := s.State.AddRelation(eps...)
  1381  	c.Assert(err, jc.ErrorIsNil)
  1382  	ru, err := rel.Unit(unitZero)
  1383  	c.Assert(err, jc.ErrorIsNil)
  1384  	err = ru.EnterScope(nil)
  1385  	c.Assert(err, jc.ErrorIsNil)
  1386  	subZero, err := s.State.Unit("logging/0")
  1387  	c.Assert(err, jc.ErrorIsNil)
  1388  
  1389  	// Check that once it's refreshed unitZero has subordinates.
  1390  	err = unitZero.Refresh()
  1391  	c.Assert(err, jc.ErrorIsNil)
  1392  	c.Assert(unitZero.SubordinateNames(), gc.DeepEquals, []string{"logging/0"})
  1393  
  1394  	// Check the subordinate unit has been assigned its principal's machine.
  1395  	id, err := subZero.AssignedMachineId()
  1396  	c.Assert(err, jc.ErrorIsNil)
  1397  	c.Assert(id, gc.Equals, m.Id())
  1398  }
  1399  
  1400  func (s *ServiceSuite) TestAddUnitWhenNotAlive(c *gc.C) {
  1401  	u, err := s.mysql.AddUnit()
  1402  	c.Assert(err, jc.ErrorIsNil)
  1403  	err = s.mysql.Destroy()
  1404  	c.Assert(err, jc.ErrorIsNil)
  1405  	_, err = s.mysql.AddUnit()
  1406  	c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql": service is not alive`)
  1407  	err = u.EnsureDead()
  1408  	c.Assert(err, jc.ErrorIsNil)
  1409  	err = u.Remove()
  1410  	c.Assert(err, jc.ErrorIsNil)
  1411  	_, err = s.mysql.AddUnit()
  1412  	c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql": service "mysql" not found`)
  1413  }
  1414  
  1415  func (s *ServiceSuite) TestReadUnit(c *gc.C) {
  1416  	_, err := s.mysql.AddUnit()
  1417  	c.Assert(err, jc.ErrorIsNil)
  1418  	_, err = s.mysql.AddUnit()
  1419  	c.Assert(err, jc.ErrorIsNil)
  1420  
  1421  	// Check that retrieving a unit from state works correctly.
  1422  	unit, err := s.State.Unit("mysql/0")
  1423  	c.Assert(err, jc.ErrorIsNil)
  1424  	c.Assert(unit.Name(), gc.Equals, "mysql/0")
  1425  
  1426  	// Check that retrieving a non-existent or an invalidly
  1427  	// named unit fail nicely.
  1428  	unit, err = s.State.Unit("mysql")
  1429  	c.Assert(err, gc.ErrorMatches, `"mysql" is not a valid unit name`)
  1430  	unit, err = s.State.Unit("mysql/0/0")
  1431  	c.Assert(err, gc.ErrorMatches, `"mysql/0/0" is not a valid unit name`)
  1432  	unit, err = s.State.Unit("pressword/0")
  1433  	c.Assert(err, gc.ErrorMatches, `unit "pressword/0" not found`)
  1434  
  1435  	units, err := s.mysql.AllUnits()
  1436  	c.Assert(err, jc.ErrorIsNil)
  1437  	c.Assert(sortedUnitNames(units), gc.DeepEquals, []string{"mysql/0", "mysql/1"})
  1438  }
  1439  
  1440  func (s *ServiceSuite) TestReadUnitWhenDying(c *gc.C) {
  1441  	// Test that we can still read units when the service is Dying...
  1442  	unit, err := s.mysql.AddUnit()
  1443  	c.Assert(err, jc.ErrorIsNil)
  1444  	preventUnitDestroyRemove(c, unit)
  1445  	err = s.mysql.Destroy()
  1446  	c.Assert(err, jc.ErrorIsNil)
  1447  	_, err = s.mysql.AllUnits()
  1448  	c.Assert(err, jc.ErrorIsNil)
  1449  	_, err = s.State.Unit("mysql/0")
  1450  	c.Assert(err, jc.ErrorIsNil)
  1451  
  1452  	// ...and when those units are Dying or Dead...
  1453  	testWhenDying(c, unit, noErr, noErr, func() error {
  1454  		_, err := s.mysql.AllUnits()
  1455  		return err
  1456  	}, func() error {
  1457  		_, err := s.State.Unit("mysql/0")
  1458  		return err
  1459  	})
  1460  
  1461  	// ...and even, in a very limited way, when the service itself is removed.
  1462  	removeAllUnits(c, s.mysql)
  1463  	_, err = s.mysql.AllUnits()
  1464  	c.Assert(err, jc.ErrorIsNil)
  1465  }
  1466  
  1467  func (s *ServiceSuite) TestDestroySimple(c *gc.C) {
  1468  	err := s.mysql.Destroy()
  1469  	c.Assert(err, jc.ErrorIsNil)
  1470  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
  1471  	err = s.mysql.Refresh()
  1472  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1473  }
  1474  
  1475  func (s *ServiceSuite) TestDestroyStillHasUnits(c *gc.C) {
  1476  	unit, err := s.mysql.AddUnit()
  1477  	c.Assert(err, jc.ErrorIsNil)
  1478  	err = s.mysql.Destroy()
  1479  	c.Assert(err, jc.ErrorIsNil)
  1480  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
  1481  
  1482  	err = unit.EnsureDead()
  1483  	c.Assert(err, jc.ErrorIsNil)
  1484  	err = s.mysql.Refresh()
  1485  	c.Assert(err, jc.ErrorIsNil)
  1486  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
  1487  
  1488  	err = unit.Remove()
  1489  	c.Assert(err, jc.ErrorIsNil)
  1490  	err = s.mysql.Refresh()
  1491  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1492  }
  1493  
  1494  func (s *ServiceSuite) TestDestroyOnceHadUnits(c *gc.C) {
  1495  	unit, err := s.mysql.AddUnit()
  1496  	c.Assert(err, jc.ErrorIsNil)
  1497  	err = unit.EnsureDead()
  1498  	c.Assert(err, jc.ErrorIsNil)
  1499  	err = unit.Remove()
  1500  	c.Assert(err, jc.ErrorIsNil)
  1501  
  1502  	err = s.mysql.Destroy()
  1503  	c.Assert(err, jc.ErrorIsNil)
  1504  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
  1505  	err = s.mysql.Refresh()
  1506  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1507  }
  1508  
  1509  func (s *ServiceSuite) TestDestroyStaleNonZeroUnitCount(c *gc.C) {
  1510  	unit, err := s.mysql.AddUnit()
  1511  	c.Assert(err, jc.ErrorIsNil)
  1512  	err = s.mysql.Refresh()
  1513  	c.Assert(err, jc.ErrorIsNil)
  1514  	err = unit.EnsureDead()
  1515  	c.Assert(err, jc.ErrorIsNil)
  1516  	err = unit.Remove()
  1517  	c.Assert(err, jc.ErrorIsNil)
  1518  
  1519  	err = s.mysql.Destroy()
  1520  	c.Assert(err, jc.ErrorIsNil)
  1521  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
  1522  	err = s.mysql.Refresh()
  1523  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1524  }
  1525  
  1526  func (s *ServiceSuite) TestDestroyStaleZeroUnitCount(c *gc.C) {
  1527  	unit, err := s.mysql.AddUnit()
  1528  	c.Assert(err, jc.ErrorIsNil)
  1529  
  1530  	err = s.mysql.Destroy()
  1531  	c.Assert(err, jc.ErrorIsNil)
  1532  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
  1533  
  1534  	err = s.mysql.Refresh()
  1535  	c.Assert(err, jc.ErrorIsNil)
  1536  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
  1537  
  1538  	err = unit.EnsureDead()
  1539  	c.Assert(err, jc.ErrorIsNil)
  1540  	err = s.mysql.Refresh()
  1541  	c.Assert(err, jc.ErrorIsNil)
  1542  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
  1543  
  1544  	err = unit.Remove()
  1545  	c.Assert(err, jc.ErrorIsNil)
  1546  	err = s.mysql.Refresh()
  1547  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1548  }
  1549  
  1550  func (s *ServiceSuite) TestDestroyWithRemovableRelation(c *gc.C) {
  1551  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  1552  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  1553  	c.Assert(err, jc.ErrorIsNil)
  1554  	rel, err := s.State.AddRelation(eps...)
  1555  	c.Assert(err, jc.ErrorIsNil)
  1556  
  1557  	// Destroy a service with no units in relation scope; check service and
  1558  	// unit removed.
  1559  	err = wordpress.Destroy()
  1560  	c.Assert(err, jc.ErrorIsNil)
  1561  	err = wordpress.Refresh()
  1562  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1563  	err = rel.Refresh()
  1564  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1565  }
  1566  
  1567  func (s *ServiceSuite) TestDestroyWithReferencedRelation(c *gc.C) {
  1568  	s.assertDestroyWithReferencedRelation(c, true)
  1569  }
  1570  
  1571  func (s *ServiceSuite) TestDestroyWithReferencedRelationStaleCount(c *gc.C) {
  1572  	s.assertDestroyWithReferencedRelation(c, false)
  1573  }
  1574  
  1575  func (s *ServiceSuite) assertDestroyWithReferencedRelation(c *gc.C, refresh bool) {
  1576  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  1577  	eps, err := s.State.InferEndpoints("wordpress", "mysql")
  1578  	c.Assert(err, jc.ErrorIsNil)
  1579  	rel0, err := s.State.AddRelation(eps...)
  1580  	c.Assert(err, jc.ErrorIsNil)
  1581  
  1582  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
  1583  	eps, err = s.State.InferEndpoints("logging", "mysql")
  1584  	c.Assert(err, jc.ErrorIsNil)
  1585  	rel1, err := s.State.AddRelation(eps...)
  1586  	c.Assert(err, jc.ErrorIsNil)
  1587  
  1588  	// Add a separate reference to the first relation.
  1589  	unit, err := wordpress.AddUnit()
  1590  	c.Assert(err, jc.ErrorIsNil)
  1591  	ru, err := rel0.Unit(unit)
  1592  	c.Assert(err, jc.ErrorIsNil)
  1593  	err = ru.EnterScope(nil)
  1594  	c.Assert(err, jc.ErrorIsNil)
  1595  
  1596  	// Optionally update the service document to get correct relation counts.
  1597  	if refresh {
  1598  		err = s.mysql.Destroy()
  1599  		c.Assert(err, jc.ErrorIsNil)
  1600  	}
  1601  
  1602  	// Destroy, and check that the first relation becomes Dying...
  1603  	err = s.mysql.Destroy()
  1604  	c.Assert(err, jc.ErrorIsNil)
  1605  	err = rel0.Refresh()
  1606  	c.Assert(err, jc.ErrorIsNil)
  1607  	c.Assert(rel0.Life(), gc.Equals, state.Dying)
  1608  
  1609  	// ...while the second is removed directly.
  1610  	err = rel1.Refresh()
  1611  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1612  
  1613  	// Drop the last reference to the first relation; check the relation and
  1614  	// the service are are both removed.
  1615  	err = ru.LeaveScope()
  1616  	c.Assert(err, jc.ErrorIsNil)
  1617  	err = s.mysql.Refresh()
  1618  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1619  	err = rel0.Refresh()
  1620  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1621  }
  1622  
  1623  func (s *ServiceSuite) TestDestroyQueuesUnitCleanup(c *gc.C) {
  1624  	// Add 5 units; block quick-remove of mysql/1 and mysql/3
  1625  	units := make([]*state.Unit, 5)
  1626  	for i := range units {
  1627  		unit, err := s.mysql.AddUnit()
  1628  		c.Assert(err, jc.ErrorIsNil)
  1629  		units[i] = unit
  1630  		if i%2 != 0 {
  1631  			preventUnitDestroyRemove(c, unit)
  1632  		}
  1633  	}
  1634  
  1635  	// Check state is clean.
  1636  	dirty, err := s.State.NeedsCleanup()
  1637  	c.Assert(err, jc.ErrorIsNil)
  1638  	c.Assert(dirty, jc.IsFalse)
  1639  
  1640  	// Destroy mysql, and check units are not touched.
  1641  	err = s.mysql.Destroy()
  1642  	c.Assert(err, jc.ErrorIsNil)
  1643  	for _, unit := range units {
  1644  		assertLife(c, unit, state.Alive)
  1645  	}
  1646  
  1647  	// Check a cleanup doc was added.
  1648  	dirty, err = s.State.NeedsCleanup()
  1649  	c.Assert(err, jc.ErrorIsNil)
  1650  	c.Assert(dirty, jc.IsTrue)
  1651  
  1652  	// Run the cleanup and check the units.
  1653  	err = s.State.Cleanup()
  1654  	c.Assert(err, jc.ErrorIsNil)
  1655  	for i, unit := range units {
  1656  		if i%2 != 0 {
  1657  			assertLife(c, unit, state.Dying)
  1658  		} else {
  1659  			assertRemoved(c, unit)
  1660  		}
  1661  	}
  1662  
  1663  	// Check for queued unit cleanups, and run them.
  1664  	dirty, err = s.State.NeedsCleanup()
  1665  	c.Assert(err, jc.ErrorIsNil)
  1666  	c.Assert(dirty, jc.IsTrue)
  1667  	err = s.State.Cleanup()
  1668  	c.Assert(err, jc.ErrorIsNil)
  1669  
  1670  	// Check we're now clean.
  1671  	dirty, err = s.State.NeedsCleanup()
  1672  	c.Assert(err, jc.ErrorIsNil)
  1673  	c.Assert(dirty, jc.IsFalse)
  1674  }
  1675  
  1676  func (s *ServiceSuite) TestRemoveServiceMachine(c *gc.C) {
  1677  	unit, err := s.mysql.AddUnit()
  1678  	c.Assert(err, jc.ErrorIsNil)
  1679  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  1680  	c.Assert(err, jc.ErrorIsNil)
  1681  	c.Assert(unit.AssignToMachine(machine), gc.IsNil)
  1682  
  1683  	c.Assert(s.mysql.Destroy(), gc.IsNil)
  1684  	assertLife(c, s.mysql, state.Dying)
  1685  
  1686  	// Service.Destroy adds units to cleanup, make it happen now.
  1687  	c.Assert(s.State.Cleanup(), gc.IsNil)
  1688  
  1689  	c.Assert(unit.Refresh(), jc.Satisfies, errors.IsNotFound)
  1690  	assertLife(c, machine, state.Dying)
  1691  }
  1692  
  1693  func (s *ServiceSuite) TestReadUnitWithChangingState(c *gc.C) {
  1694  	// Check that reading a unit after removing the service
  1695  	// fails nicely.
  1696  	err := s.mysql.Destroy()
  1697  	c.Assert(err, jc.ErrorIsNil)
  1698  	err = s.mysql.Refresh()
  1699  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1700  	_, err = s.State.Unit("mysql/0")
  1701  	c.Assert(err, gc.ErrorMatches, `unit "mysql/0" not found`)
  1702  }
  1703  
  1704  func uint64p(val uint64) *uint64 {
  1705  	return &val
  1706  }
  1707  
  1708  func (s *ServiceSuite) TestConstraints(c *gc.C) {
  1709  	// Constraints are initially empty (for now).
  1710  	cons, err := s.mysql.Constraints()
  1711  	c.Assert(err, jc.ErrorIsNil)
  1712  	c.Assert(&cons, jc.Satisfies, constraints.IsEmpty)
  1713  
  1714  	// Constraints can be set.
  1715  	cons2 := constraints.Value{Mem: uint64p(4096)}
  1716  	err = s.mysql.SetConstraints(cons2)
  1717  	cons3, err := s.mysql.Constraints()
  1718  	c.Assert(err, jc.ErrorIsNil)
  1719  	c.Assert(cons3, gc.DeepEquals, cons2)
  1720  
  1721  	// Constraints are completely overwritten when re-set.
  1722  	cons4 := constraints.Value{CpuPower: uint64p(750)}
  1723  	err = s.mysql.SetConstraints(cons4)
  1724  	c.Assert(err, jc.ErrorIsNil)
  1725  	cons5, err := s.mysql.Constraints()
  1726  	c.Assert(err, jc.ErrorIsNil)
  1727  	c.Assert(cons5, gc.DeepEquals, cons4)
  1728  
  1729  	// Destroy the existing service; there's no way to directly assert
  1730  	// that the constraints are deleted...
  1731  	err = s.mysql.Destroy()
  1732  	c.Assert(err, jc.ErrorIsNil)
  1733  	err = s.mysql.Refresh()
  1734  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  1735  
  1736  	// ...but we can check that old constraints do not affect new services
  1737  	// with matching names.
  1738  	ch, _, err := s.mysql.Charm()
  1739  	c.Assert(err, jc.ErrorIsNil)
  1740  	mysql := s.AddTestingService(c, s.mysql.Name(), ch)
  1741  	cons6, err := mysql.Constraints()
  1742  	c.Assert(err, jc.ErrorIsNil)
  1743  	c.Assert(&cons6, jc.Satisfies, constraints.IsEmpty)
  1744  }
  1745  
  1746  func (s *ServiceSuite) TestSetInvalidConstraints(c *gc.C) {
  1747  	cons := constraints.MustParse("mem=4G instance-type=foo")
  1748  	err := s.mysql.SetConstraints(cons)
  1749  	c.Assert(err, gc.ErrorMatches, `ambiguous constraints: "instance-type" overlaps with "mem"`)
  1750  }
  1751  
  1752  func (s *ServiceSuite) TestSetUnsupportedConstraintsWarning(c *gc.C) {
  1753  	defer loggo.ResetWriters()
  1754  	logger := loggo.GetLogger("test")
  1755  	logger.SetLogLevel(loggo.DEBUG)
  1756  	var tw loggo.TestWriter
  1757  	c.Assert(loggo.RegisterWriter("constraints-tester", &tw, loggo.DEBUG), gc.IsNil)
  1758  
  1759  	cons := constraints.MustParse("mem=4G cpu-power=10")
  1760  	err := s.mysql.SetConstraints(cons)
  1761  	c.Assert(err, jc.ErrorIsNil)
  1762  	c.Assert(tw.Log(), jc.LogMatches, jc.SimpleMessages{{
  1763  		loggo.WARNING,
  1764  		`setting constraints on service "mysql": unsupported constraints: cpu-power`},
  1765  	})
  1766  	scons, err := s.mysql.Constraints()
  1767  	c.Assert(err, jc.ErrorIsNil)
  1768  	c.Assert(scons, gc.DeepEquals, cons)
  1769  }
  1770  
  1771  func (s *ServiceSuite) TestConstraintsLifecycle(c *gc.C) {
  1772  	// Dying.
  1773  	unit, err := s.mysql.AddUnit()
  1774  	c.Assert(err, jc.ErrorIsNil)
  1775  	err = s.mysql.Destroy()
  1776  	c.Assert(err, jc.ErrorIsNil)
  1777  	cons1 := constraints.MustParse("mem=1G")
  1778  	err = s.mysql.SetConstraints(cons1)
  1779  	c.Assert(err, gc.ErrorMatches, `cannot set constraints: not found or not alive`)
  1780  	scons, err := s.mysql.Constraints()
  1781  	c.Assert(err, jc.ErrorIsNil)
  1782  	c.Assert(&scons, jc.Satisfies, constraints.IsEmpty)
  1783  
  1784  	// Removed (== Dead, for a service).
  1785  	err = unit.EnsureDead()
  1786  	c.Assert(err, jc.ErrorIsNil)
  1787  	err = unit.Remove()
  1788  	c.Assert(err, jc.ErrorIsNil)
  1789  	err = s.mysql.SetConstraints(cons1)
  1790  	c.Assert(err, gc.ErrorMatches, `cannot set constraints: not found or not alive`)
  1791  	_, err = s.mysql.Constraints()
  1792  	c.Assert(err, gc.ErrorMatches, `constraints not found`)
  1793  }
  1794  
  1795  func (s *ServiceSuite) TestSubordinateConstraints(c *gc.C) {
  1796  	loggingCh := s.AddTestingCharm(c, "logging")
  1797  	logging := s.AddTestingService(c, "logging", loggingCh)
  1798  
  1799  	_, err := logging.Constraints()
  1800  	c.Assert(err, gc.Equals, state.ErrSubordinateConstraints)
  1801  
  1802  	err = logging.SetConstraints(constraints.Value{})
  1803  	c.Assert(err, gc.Equals, state.ErrSubordinateConstraints)
  1804  }
  1805  
  1806  func (s *ServiceSuite) TestWatchUnitsBulkEvents(c *gc.C) {
  1807  	// Alive unit...
  1808  	alive, err := s.mysql.AddUnit()
  1809  	c.Assert(err, jc.ErrorIsNil)
  1810  
  1811  	// Dying unit...
  1812  	dying, err := s.mysql.AddUnit()
  1813  	c.Assert(err, jc.ErrorIsNil)
  1814  	preventUnitDestroyRemove(c, dying)
  1815  	err = dying.Destroy()
  1816  	c.Assert(err, jc.ErrorIsNil)
  1817  
  1818  	// Dead unit...
  1819  	dead, err := s.mysql.AddUnit()
  1820  	c.Assert(err, jc.ErrorIsNil)
  1821  	preventUnitDestroyRemove(c, dead)
  1822  	err = dead.Destroy()
  1823  	c.Assert(err, jc.ErrorIsNil)
  1824  	err = dead.EnsureDead()
  1825  	c.Assert(err, jc.ErrorIsNil)
  1826  
  1827  	// Gone unit.
  1828  	gone, err := s.mysql.AddUnit()
  1829  	c.Assert(err, jc.ErrorIsNil)
  1830  	err = gone.Destroy()
  1831  	c.Assert(err, jc.ErrorIsNil)
  1832  
  1833  	// All except gone unit are reported in initial event.
  1834  	w := s.mysql.WatchUnits()
  1835  	defer testing.AssertStop(c, w)
  1836  	wc := testing.NewStringsWatcherC(c, s.State, w)
  1837  	wc.AssertChange(alive.Name(), dying.Name(), dead.Name())
  1838  	wc.AssertNoChange()
  1839  
  1840  	// Remove them all; alive/dying changes reported; dead never mentioned again.
  1841  	err = alive.Destroy()
  1842  	c.Assert(err, jc.ErrorIsNil)
  1843  	err = dying.EnsureDead()
  1844  	c.Assert(err, jc.ErrorIsNil)
  1845  	err = dying.Remove()
  1846  	c.Assert(err, jc.ErrorIsNil)
  1847  	err = dead.Remove()
  1848  	c.Assert(err, jc.ErrorIsNil)
  1849  	wc.AssertChange(alive.Name(), dying.Name())
  1850  	wc.AssertNoChange()
  1851  }
  1852  
  1853  func (s *ServiceSuite) TestWatchUnitsLifecycle(c *gc.C) {
  1854  	// Empty initial event when no units.
  1855  	w := s.mysql.WatchUnits()
  1856  	defer testing.AssertStop(c, w)
  1857  	wc := testing.NewStringsWatcherC(c, s.State, w)
  1858  	wc.AssertChange()
  1859  	wc.AssertNoChange()
  1860  
  1861  	// Create one unit, check one change.
  1862  	quick, err := s.mysql.AddUnit()
  1863  	c.Assert(err, jc.ErrorIsNil)
  1864  	wc.AssertChange(quick.Name())
  1865  	wc.AssertNoChange()
  1866  
  1867  	// Destroy that unit (short-circuited to removal), check one change.
  1868  	err = quick.Destroy()
  1869  	c.Assert(err, jc.ErrorIsNil)
  1870  	wc.AssertChange(quick.Name())
  1871  	wc.AssertNoChange()
  1872  
  1873  	// Create another, check one change.
  1874  	slow, err := s.mysql.AddUnit()
  1875  	c.Assert(err, jc.ErrorIsNil)
  1876  	wc.AssertChange(slow.Name())
  1877  	wc.AssertNoChange()
  1878  
  1879  	// Change unit itself, no change.
  1880  	preventUnitDestroyRemove(c, slow)
  1881  	wc.AssertNoChange()
  1882  
  1883  	// Make unit Dying, change detected.
  1884  	err = slow.Destroy()
  1885  	c.Assert(err, jc.ErrorIsNil)
  1886  	wc.AssertChange(slow.Name())
  1887  	wc.AssertNoChange()
  1888  
  1889  	// Make unit Dead, change detected.
  1890  	err = slow.EnsureDead()
  1891  	c.Assert(err, jc.ErrorIsNil)
  1892  	wc.AssertChange(slow.Name())
  1893  	wc.AssertNoChange()
  1894  
  1895  	// Remove unit, final change not detected.
  1896  	err = slow.Remove()
  1897  	c.Assert(err, jc.ErrorIsNil)
  1898  	wc.AssertNoChange()
  1899  }
  1900  
  1901  func (s *ServiceSuite) TestWatchRelations(c *gc.C) {
  1902  	// TODO(fwereade) split this test up a bit.
  1903  	w := s.mysql.WatchRelations()
  1904  	defer testing.AssertStop(c, w)
  1905  	wc := testing.NewStringsWatcherC(c, s.State, w)
  1906  	wc.AssertChange()
  1907  	wc.AssertNoChange()
  1908  
  1909  	// Add a relation; check change.
  1910  	mysqlep, err := s.mysql.Endpoint("server")
  1911  	c.Assert(err, jc.ErrorIsNil)
  1912  	wpch := s.AddTestingCharm(c, "wordpress")
  1913  	wpi := 0
  1914  	addRelation := func() *state.Relation {
  1915  		name := fmt.Sprintf("wp%d", wpi)
  1916  		wpi++
  1917  		wp := s.AddTestingService(c, name, wpch)
  1918  		wpep, err := wp.Endpoint("db")
  1919  		c.Assert(err, jc.ErrorIsNil)
  1920  		rel, err := s.State.AddRelation(mysqlep, wpep)
  1921  		c.Assert(err, jc.ErrorIsNil)
  1922  		return rel
  1923  	}
  1924  	rel0 := addRelation()
  1925  	wc.AssertChange(rel0.String())
  1926  	wc.AssertNoChange()
  1927  
  1928  	// Add another relation; check change.
  1929  	rel1 := addRelation()
  1930  	wc.AssertChange(rel1.String())
  1931  	wc.AssertNoChange()
  1932  
  1933  	// Destroy a relation; check change.
  1934  	err = rel0.Destroy()
  1935  	c.Assert(err, jc.ErrorIsNil)
  1936  	wc.AssertChange(rel0.String())
  1937  	wc.AssertNoChange()
  1938  
  1939  	// Stop watcher; check change chan is closed.
  1940  	testing.AssertStop(c, w)
  1941  	wc.AssertClosed()
  1942  
  1943  	// Add a new relation; start a new watcher; check initial event.
  1944  	rel2 := addRelation()
  1945  	w = s.mysql.WatchRelations()
  1946  	defer testing.AssertStop(c, w)
  1947  	wc = testing.NewStringsWatcherC(c, s.State, w)
  1948  	wc.AssertChange(rel1.String(), rel2.String())
  1949  	wc.AssertNoChange()
  1950  
  1951  	// Add a unit to the new relation; check no change.
  1952  	unit, err := s.mysql.AddUnit()
  1953  	c.Assert(err, jc.ErrorIsNil)
  1954  	ru2, err := rel2.Unit(unit)
  1955  	c.Assert(err, jc.ErrorIsNil)
  1956  	err = ru2.EnterScope(nil)
  1957  	c.Assert(err, jc.ErrorIsNil)
  1958  	wc.AssertNoChange()
  1959  
  1960  	// Destroy the relation with the unit in scope, and add another; check
  1961  	// changes.
  1962  	err = rel2.Destroy()
  1963  	c.Assert(err, jc.ErrorIsNil)
  1964  	rel3 := addRelation()
  1965  	wc.AssertChange(rel2.String(), rel3.String())
  1966  	wc.AssertNoChange()
  1967  
  1968  	// Leave scope, destroying the relation, and check that change as well.
  1969  	err = ru2.LeaveScope()
  1970  	c.Assert(err, jc.ErrorIsNil)
  1971  	wc.AssertChange(rel2.String())
  1972  	wc.AssertNoChange()
  1973  
  1974  	// Watch relations on the requirer service too (exercises a
  1975  	// different path of the WatchRelations filter function)
  1976  	wpx := s.AddTestingService(c, "wpx", wpch)
  1977  	wpxWatcher := wpx.WatchRelations()
  1978  	defer testing.AssertStop(c, wpxWatcher)
  1979  	wpxWatcherC := testing.NewStringsWatcherC(c, s.State, wpxWatcher)
  1980  	wpxWatcherC.AssertChange()
  1981  	wpxWatcherC.AssertNoChange()
  1982  
  1983  	wpxep, err := wpx.Endpoint("db")
  1984  	c.Assert(err, jc.ErrorIsNil)
  1985  	relx, err := s.State.AddRelation(mysqlep, wpxep)
  1986  	c.Assert(err, jc.ErrorIsNil)
  1987  	wpxWatcherC.AssertChange(relx.String())
  1988  	wpxWatcherC.AssertNoChange()
  1989  }
  1990  
  1991  func removeAllUnits(c *gc.C, s *state.Service) {
  1992  	us, err := s.AllUnits()
  1993  	c.Assert(err, jc.ErrorIsNil)
  1994  	for _, u := range us {
  1995  		err = u.EnsureDead()
  1996  		c.Assert(err, jc.ErrorIsNil)
  1997  		err = u.Remove()
  1998  		c.Assert(err, jc.ErrorIsNil)
  1999  	}
  2000  }
  2001  
  2002  func (s *ServiceSuite) TestWatchService(c *gc.C) {
  2003  	w := s.mysql.Watch()
  2004  	defer testing.AssertStop(c, w)
  2005  
  2006  	// Initial event.
  2007  	wc := testing.NewNotifyWatcherC(c, s.State, w)
  2008  	wc.AssertOneChange()
  2009  
  2010  	// Make one change (to a separate instance), check one event.
  2011  	service, err := s.State.Service(s.mysql.Name())
  2012  	c.Assert(err, jc.ErrorIsNil)
  2013  	err = service.SetExposed()
  2014  	c.Assert(err, jc.ErrorIsNil)
  2015  	wc.AssertOneChange()
  2016  
  2017  	// Make two changes, check one event.
  2018  	err = service.ClearExposed()
  2019  	c.Assert(err, jc.ErrorIsNil)
  2020  
  2021  	cfg := state.SetCharmConfig{
  2022  		Charm:      s.charm,
  2023  		ForceUnits: true,
  2024  	}
  2025  	err = service.SetCharm(cfg)
  2026  	c.Assert(err, jc.ErrorIsNil)
  2027  	wc.AssertOneChange()
  2028  
  2029  	// Stop, check closed.
  2030  	testing.AssertStop(c, w)
  2031  	wc.AssertClosed()
  2032  
  2033  	// Remove service, start new watch, check single event.
  2034  	err = service.Destroy()
  2035  	c.Assert(err, jc.ErrorIsNil)
  2036  	w = s.mysql.Watch()
  2037  	defer testing.AssertStop(c, w)
  2038  	testing.NewNotifyWatcherC(c, s.State, w).AssertOneChange()
  2039  }
  2040  
  2041  // SCHEMACHANGE
  2042  // TODO(mattyw) remove when schema upgrades are possible
  2043  // Check that GetOwnerTag returns user-admin even
  2044  // when the service has no owner
  2045  func (s *ServiceSuite) TestOwnerTagSchemaProtection(c *gc.C) {
  2046  	service := s.AddTestingService(c, "foobar", s.charm)
  2047  	state.SetServiceOwnerTag(service, "")
  2048  	c.Assert(state.GetServiceOwnerTag(service), gc.Equals, "")
  2049  	c.Assert(service.GetOwnerTag(), gc.Equals, "user-admin")
  2050  }
  2051  
  2052  func (s *ServiceSuite) TestMetricCredentials(c *gc.C) {
  2053  	err := s.mysql.SetMetricCredentials([]byte("hello there"))
  2054  	c.Assert(err, jc.ErrorIsNil)
  2055  	c.Assert(s.mysql.MetricCredentials(), gc.DeepEquals, []byte("hello there"))
  2056  
  2057  	service, err := s.State.Service(s.mysql.Name())
  2058  	c.Assert(service.MetricCredentials(), gc.DeepEquals, []byte("hello there"))
  2059  }
  2060  
  2061  func (s *ServiceSuite) TestMetricCredentialsOnDying(c *gc.C) {
  2062  	_, err := s.mysql.AddUnit()
  2063  	c.Assert(err, jc.ErrorIsNil)
  2064  	err = s.mysql.SetMetricCredentials([]byte("set before dying"))
  2065  	c.Assert(err, jc.ErrorIsNil)
  2066  	err = s.mysql.Destroy()
  2067  	c.Assert(err, jc.ErrorIsNil)
  2068  	assertLife(c, s.mysql, state.Dying)
  2069  	err = s.mysql.SetMetricCredentials([]byte("set after dying"))
  2070  	c.Assert(err, gc.ErrorMatches, "cannot update metric credentials: service not found or not alive")
  2071  }
  2072  
  2073  func (s *ServiceSuite) testStatus(c *gc.C, status1, status2, expected status.Status) {
  2074  	u1, err := s.mysql.AddUnit()
  2075  	c.Assert(err, jc.ErrorIsNil)
  2076  	err = u1.SetStatus(status1, "status 1", nil)
  2077  	c.Assert(err, jc.ErrorIsNil)
  2078  
  2079  	u2, err := s.mysql.AddUnit()
  2080  	c.Assert(err, jc.ErrorIsNil)
  2081  	if status2 == status.StatusError {
  2082  		err = u2.SetAgentStatus(status2, "status 2", nil)
  2083  	} else {
  2084  		err = u2.SetStatus(status2, "status 2", nil)
  2085  	}
  2086  	c.Assert(err, jc.ErrorIsNil)
  2087  
  2088  	statusInfo, err := s.mysql.Status()
  2089  	c.Assert(err, jc.ErrorIsNil)
  2090  	c.Assert(statusInfo.Since, gc.NotNil)
  2091  	statusInfo.Since = nil
  2092  	c.Assert(statusInfo, jc.DeepEquals, status.StatusInfo{
  2093  		Status:  expected,
  2094  		Message: "status 2",
  2095  		Data:    map[string]interface{}{},
  2096  	})
  2097  }
  2098  
  2099  func (s *ServiceSuite) TestStatus(c *gc.C) {
  2100  	for _, t := range []struct{ status1, status2, expected status.Status }{
  2101  		{status.StatusActive, status.StatusWaiting, status.StatusWaiting},
  2102  		{status.StatusMaintenance, status.StatusWaiting, status.StatusWaiting},
  2103  		{status.StatusActive, status.StatusBlocked, status.StatusBlocked},
  2104  		{status.StatusWaiting, status.StatusBlocked, status.StatusBlocked},
  2105  		{status.StatusMaintenance, status.StatusBlocked, status.StatusBlocked},
  2106  		{status.StatusMaintenance, status.StatusError, status.StatusError},
  2107  		{status.StatusBlocked, status.StatusError, status.StatusError},
  2108  		{status.StatusWaiting, status.StatusError, status.StatusError},
  2109  		{status.StatusActive, status.StatusError, status.StatusError},
  2110  	} {
  2111  		s.testStatus(c, t.status1, t.status2, t.expected)
  2112  	}
  2113  }
  2114  
  2115  const oneRequiredStorageMeta = `
  2116  storage:
  2117    data0:
  2118      type: block
  2119  `
  2120  
  2121  const oneOptionalStorageMeta = `
  2122  storage:
  2123    data0:
  2124      type: block
  2125      multiple:
  2126        range: 0-
  2127  `
  2128  
  2129  const twoRequiredStorageMeta = `
  2130  storage:
  2131    data0:
  2132      type: block
  2133    data1:
  2134      type: block
  2135  `
  2136  
  2137  const twoOptionalStorageMeta = `
  2138  storage:
  2139    data0:
  2140      type: block
  2141      multiple:
  2142        range: 0-
  2143    data1:
  2144      type: block
  2145      multiple:
  2146        range: 0-
  2147  `
  2148  
  2149  const oneRequiredFilesystemStorageMeta = `
  2150  storage:
  2151    data0:
  2152      type: filesystem
  2153  `
  2154  
  2155  const oneRequiredSharedStorageMeta = `
  2156  storage:
  2157    data0:
  2158      type: block
  2159      shared: true
  2160  `
  2161  
  2162  const oneRequiredReadOnlyStorageMeta = `
  2163  storage:
  2164    data0:
  2165      type: block
  2166      read-only: true
  2167  `
  2168  
  2169  const oneRequiredLocationStorageMeta = `
  2170  storage:
  2171    data0:
  2172      type: filesystem
  2173      location: /srv
  2174  `
  2175  
  2176  const oneMultipleLocationStorageMeta = `
  2177  storage:
  2178    data0:
  2179      type: filesystem
  2180      location: /srv
  2181      multiple:
  2182        range: 1-
  2183  `
  2184  
  2185  func storageRange(min, max int) string {
  2186  	var minStr, maxStr string
  2187  	if min > 0 {
  2188  		minStr = fmt.Sprint(min)
  2189  	}
  2190  	if max > 0 {
  2191  		maxStr = fmt.Sprint(max)
  2192  	}
  2193  	return fmt.Sprintf(`
  2194      multiple:
  2195        range: %s-%s
  2196  `[1:], minStr, maxStr)
  2197  }
  2198  
  2199  func (s *ServiceSuite) setCharmFromMeta(c *gc.C, oldMeta, newMeta string) error {
  2200  	registry.RegisterEnvironStorageProviders("someprovider", provider.LoopProviderType)
  2201  	oldCh := s.AddMetaCharm(c, "mysql", oldMeta, 2)
  2202  	newCh := s.AddMetaCharm(c, "mysql", newMeta, 3)
  2203  	svc := s.AddTestingService(c, "test", oldCh)
  2204  
  2205  	cfg := state.SetCharmConfig{Charm: newCh}
  2206  	return svc.SetCharm(cfg)
  2207  }
  2208  
  2209  func (s *ServiceSuite) TestSetCharmStorageRemoved(c *gc.C) {
  2210  	err := s.setCharmFromMeta(c,
  2211  		mysqlBaseMeta+twoOptionalStorageMeta,
  2212  		mysqlBaseMeta+oneOptionalStorageMeta,
  2213  	)
  2214  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": storage "data1" removed`)
  2215  }
  2216  
  2217  func (s *ServiceSuite) TestSetCharmRequiredStorageAdded(c *gc.C) {
  2218  	err := s.setCharmFromMeta(c,
  2219  		mysqlBaseMeta+oneRequiredStorageMeta,
  2220  		mysqlBaseMeta+twoRequiredStorageMeta,
  2221  	)
  2222  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": required storage "data1" added`)
  2223  }
  2224  
  2225  func (s *ServiceSuite) TestSetCharmOptionalStorageAdded(c *gc.C) {
  2226  	err := s.setCharmFromMeta(c,
  2227  		mysqlBaseMeta+oneRequiredStorageMeta,
  2228  		mysqlBaseMeta+twoOptionalStorageMeta,
  2229  	)
  2230  	c.Assert(err, jc.ErrorIsNil)
  2231  }
  2232  
  2233  func (s *ServiceSuite) TestSetCharmStorageCountMinDecreased(c *gc.C) {
  2234  	err := s.setCharmFromMeta(c,
  2235  		mysqlBaseMeta+oneRequiredStorageMeta+storageRange(2, 3),
  2236  		mysqlBaseMeta+oneRequiredStorageMeta+storageRange(1, 3),
  2237  	)
  2238  	c.Assert(err, jc.ErrorIsNil)
  2239  }
  2240  
  2241  func (s *ServiceSuite) TestSetCharmStorageCountMinIncreased(c *gc.C) {
  2242  	err := s.setCharmFromMeta(c,
  2243  		mysqlBaseMeta+oneRequiredStorageMeta+storageRange(1, 3),
  2244  		mysqlBaseMeta+oneRequiredStorageMeta+storageRange(2, 3),
  2245  	)
  2246  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": existing storage "data0" range contracted: min increased from 1 to 2`)
  2247  }
  2248  
  2249  func (s *ServiceSuite) TestSetCharmStorageCountMaxDecreased(c *gc.C) {
  2250  	err := s.setCharmFromMeta(c,
  2251  		mysqlBaseMeta+oneRequiredStorageMeta+storageRange(1, 2),
  2252  		mysqlBaseMeta+oneRequiredStorageMeta+storageRange(1, 1),
  2253  	)
  2254  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": existing storage "data0" range contracted: max decreased from 2 to 1`)
  2255  }
  2256  
  2257  func (s *ServiceSuite) TestSetCharmStorageCountMaxUnboundedToBounded(c *gc.C) {
  2258  	err := s.setCharmFromMeta(c,
  2259  		mysqlBaseMeta+oneRequiredStorageMeta+storageRange(1, -1),
  2260  		mysqlBaseMeta+oneRequiredStorageMeta+storageRange(1, 999),
  2261  	)
  2262  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": existing storage "data0" range contracted: max decreased from \<unbounded\> to 999`)
  2263  }
  2264  
  2265  func (s *ServiceSuite) TestSetCharmStorageTypeChanged(c *gc.C) {
  2266  	err := s.setCharmFromMeta(c,
  2267  		mysqlBaseMeta+oneRequiredStorageMeta,
  2268  		mysqlBaseMeta+oneRequiredFilesystemStorageMeta,
  2269  	)
  2270  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": existing storage "data0" type changed from "block" to "filesystem"`)
  2271  }
  2272  
  2273  func (s *ServiceSuite) TestSetCharmStorageSharedChanged(c *gc.C) {
  2274  	err := s.setCharmFromMeta(c,
  2275  		mysqlBaseMeta+oneRequiredStorageMeta,
  2276  		mysqlBaseMeta+oneRequiredSharedStorageMeta,
  2277  	)
  2278  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": existing storage "data0" shared changed from false to true`)
  2279  }
  2280  
  2281  func (s *ServiceSuite) TestSetCharmStorageReadOnlyChanged(c *gc.C) {
  2282  	err := s.setCharmFromMeta(c,
  2283  		mysqlBaseMeta+oneRequiredStorageMeta,
  2284  		mysqlBaseMeta+oneRequiredReadOnlyStorageMeta,
  2285  	)
  2286  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": existing storage "data0" read-only changed from false to true`)
  2287  }
  2288  
  2289  func (s *ServiceSuite) TestSetCharmStorageLocationChanged(c *gc.C) {
  2290  	err := s.setCharmFromMeta(c,
  2291  		mysqlBaseMeta+oneRequiredFilesystemStorageMeta,
  2292  		mysqlBaseMeta+oneRequiredLocationStorageMeta,
  2293  	)
  2294  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": existing storage "data0" location changed from "" to "/srv"`)
  2295  }
  2296  
  2297  func (s *ServiceSuite) TestSetCharmStorageWithLocationSingletonToMultipleAdded(c *gc.C) {
  2298  	err := s.setCharmFromMeta(c,
  2299  		mysqlBaseMeta+oneRequiredLocationStorageMeta,
  2300  		mysqlBaseMeta+oneMultipleLocationStorageMeta,
  2301  	)
  2302  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "test" to charm "mysql": existing storage "data0" with location changed from singleton to multiple`)
  2303  }
  2304  
  2305  func (s *ServiceSuite) assertServiceRemovedWithItsBindings(c *gc.C, service *state.Service) {
  2306  	// Removing the service removes the bindings with it.
  2307  	err := service.Destroy()
  2308  	c.Assert(err, jc.ErrorIsNil)
  2309  	err = service.Refresh()
  2310  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
  2311  	state.AssertEndpointBindingsNotFoundForService(c, service)
  2312  }
  2313  
  2314  func (s *ServiceSuite) TestEndpointBindingsReturnsDefaultsWhenNotFound(c *gc.C) {
  2315  	ch := s.AddMetaCharm(c, "mysql", metaBase, 42)
  2316  	service := s.AddTestingServiceWithBindings(c, "yoursql", ch, nil)
  2317  	state.RemoveEndpointBindingsForService(c, service)
  2318  
  2319  	s.assertServiceHasOnlyDefaultEndpointBindings(c, service)
  2320  }
  2321  
  2322  func (s *ServiceSuite) assertServiceHasOnlyDefaultEndpointBindings(c *gc.C, service *state.Service) {
  2323  	charm, _, err := service.Charm()
  2324  	c.Assert(err, jc.ErrorIsNil)
  2325  
  2326  	knownEndpoints := set.NewStrings()
  2327  	allBindings := state.DefaultEndpointBindingsForCharm(charm.Meta())
  2328  	for endpoint, _ := range allBindings {
  2329  		knownEndpoints.Add(endpoint)
  2330  	}
  2331  
  2332  	setBindings, err := service.EndpointBindings()
  2333  	c.Assert(err, jc.ErrorIsNil)
  2334  	c.Assert(setBindings, gc.NotNil)
  2335  
  2336  	for endpoint, space := range setBindings {
  2337  		c.Check(endpoint, gc.Not(gc.Equals), "")
  2338  		c.Check(knownEndpoints.Contains(endpoint), jc.IsTrue)
  2339  		c.Check(space, gc.Equals, "", gc.Commentf("expected empty space for endpoint %q, got %q", endpoint, space))
  2340  	}
  2341  }
  2342  
  2343  func (s *ServiceSuite) TestEndpointBindingsJustDefaults(c *gc.C) {
  2344  	// With unspecified bindings, all endpoints are explicitly bound to the
  2345  	// default space when saved in state.
  2346  	ch := s.AddMetaCharm(c, "mysql", metaBase, 42)
  2347  	service := s.AddTestingServiceWithBindings(c, "yoursql", ch, nil)
  2348  
  2349  	s.assertServiceHasOnlyDefaultEndpointBindings(c, service)
  2350  	s.assertServiceRemovedWithItsBindings(c, service)
  2351  }
  2352  
  2353  func (s *ServiceSuite) TestEndpointBindingsWithExplictOverrides(c *gc.C) {
  2354  	_, err := s.State.AddSpace("db", "", nil, true)
  2355  	c.Assert(err, jc.ErrorIsNil)
  2356  	_, err = s.State.AddSpace("ha", "", nil, false)
  2357  	c.Assert(err, jc.ErrorIsNil)
  2358  
  2359  	bindings := map[string]string{
  2360  		"server":  "db",
  2361  		"cluster": "ha",
  2362  	}
  2363  	ch := s.AddMetaCharm(c, "mysql", metaBase, 42)
  2364  	service := s.AddTestingServiceWithBindings(c, "yoursql", ch, bindings)
  2365  
  2366  	setBindings, err := service.EndpointBindings()
  2367  	c.Assert(err, jc.ErrorIsNil)
  2368  	c.Assert(setBindings, jc.DeepEquals, map[string]string{
  2369  		"server":  "db",
  2370  		"client":  "",
  2371  		"cluster": "ha",
  2372  	})
  2373  
  2374  	s.assertServiceRemovedWithItsBindings(c, service)
  2375  }
  2376  
  2377  func (s *ServiceSuite) TestSetCharmExtraBindingsUseDefaults(c *gc.C) {
  2378  	_, err := s.State.AddSpace("db", "", nil, true)
  2379  	c.Assert(err, jc.ErrorIsNil)
  2380  
  2381  	oldCharm := s.AddMetaCharm(c, "mysql", metaDifferentProvider, 42)
  2382  	oldBindings := map[string]string{
  2383  		"kludge": "db",
  2384  		"client": "db",
  2385  	}
  2386  	service := s.AddTestingServiceWithBindings(c, "yoursql", oldCharm, oldBindings)
  2387  	setBindings, err := service.EndpointBindings()
  2388  	c.Assert(err, jc.ErrorIsNil)
  2389  	effectiveOld := map[string]string{
  2390  		"kludge":  "db",
  2391  		"client":  "db",
  2392  		"cluster": "",
  2393  	}
  2394  	c.Assert(setBindings, jc.DeepEquals, effectiveOld)
  2395  
  2396  	newCharm := s.AddMetaCharm(c, "mysql", metaExtraEndpoints, 43)
  2397  
  2398  	cfg := state.SetCharmConfig{Charm: newCharm}
  2399  	err = service.SetCharm(cfg)
  2400  	c.Assert(err, jc.ErrorIsNil)
  2401  	setBindings, err = service.EndpointBindings()
  2402  	c.Assert(err, jc.ErrorIsNil)
  2403  	effectiveNew := map[string]string{
  2404  		// These two should be preserved from oldCharm.
  2405  		"client":  "db",
  2406  		"cluster": "",
  2407  		// "kludge" is missing in newMeta, "server" is new and gets the default.
  2408  		"server": "",
  2409  		// All the remaining are new and use the empty default.
  2410  		"foo":  "",
  2411  		"baz":  "",
  2412  		"just": "",
  2413  	}
  2414  	c.Assert(setBindings, jc.DeepEquals, effectiveNew)
  2415  
  2416  	s.assertServiceRemovedWithItsBindings(c, service)
  2417  }
  2418  
  2419  func (s *ServiceSuite) TestSetCharmHandlesMissingBindingsAsDefaults(c *gc.C) {
  2420  	oldCharm := s.AddMetaCharm(c, "mysql", metaDifferentProvider, 69)
  2421  	service := s.AddTestingServiceWithBindings(c, "theirsql", oldCharm, nil)
  2422  	state.RemoveEndpointBindingsForService(c, service)
  2423  
  2424  	newCharm := s.AddMetaCharm(c, "mysql", metaExtraEndpoints, 70)
  2425  
  2426  	cfg := state.SetCharmConfig{Charm: newCharm}
  2427  	err := service.SetCharm(cfg)
  2428  	c.Assert(err, jc.ErrorIsNil)
  2429  	setBindings, err := service.EndpointBindings()
  2430  	c.Assert(err, jc.ErrorIsNil)
  2431  	effectiveNew := map[string]string{
  2432  		// The following two exist for both oldCharm and newCharm.
  2433  		"client":  "",
  2434  		"cluster": "",
  2435  		// "kludge" is missing in newMeta, "server" is new and gets the default.
  2436  		"server": "",
  2437  		// All the remaining are new and use the empty default.
  2438  		"foo":  "",
  2439  		"baz":  "",
  2440  		"just": "",
  2441  	}
  2442  	c.Assert(setBindings, jc.DeepEquals, effectiveNew)
  2443  
  2444  	s.assertServiceRemovedWithItsBindings(c, service)
  2445  }