launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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  	"labix.org/v2/mgo"
    11  	errgo "launchpad.net/errgo/errors"
    12  	gc "launchpad.net/gocheck"
    13  
    14  	"launchpad.net/juju-core/charm"
    15  	"launchpad.net/juju-core/constraints"
    16  	"launchpad.net/juju-core/errors"
    17  	"launchpad.net/juju-core/state"
    18  	"launchpad.net/juju-core/state/testing"
    19  	jc "launchpad.net/juju-core/testing/checkers"
    20  )
    21  
    22  type ServiceSuite struct {
    23  	ConnSuite
    24  	charm *state.Charm
    25  	mysql *state.Service
    26  }
    27  
    28  var _ = gc.Suite(&ServiceSuite{})
    29  
    30  func (s *ServiceSuite) SetUpTest(c *gc.C) {
    31  	s.ConnSuite.SetUpTest(c)
    32  	s.charm = s.AddTestingCharm(c, "mysql")
    33  	s.mysql = s.AddTestingService(c, "mysql", s.charm)
    34  }
    35  
    36  func (s *ServiceSuite) TestSetCharm(c *gc.C) {
    37  	ch, force, err := s.mysql.Charm()
    38  	c.Assert(err, gc.IsNil)
    39  	c.Assert(ch.URL(), gc.DeepEquals, s.charm.URL())
    40  	c.Assert(force, gc.Equals, false)
    41  	url, force := s.mysql.CharmURL()
    42  	c.Assert(url, gc.DeepEquals, s.charm.URL())
    43  	c.Assert(force, gc.Equals, false)
    44  
    45  	// Add a compatible charm and force it.
    46  	sch := s.AddMetaCharm(c, "mysql", metaBase, 2) // revno 1 is used by SetUpSuite
    47  	err = s.mysql.SetCharm(sch, true)
    48  	c.Assert(err, gc.IsNil)
    49  	ch, force, err = s.mysql.Charm()
    50  	c.Assert(err, gc.IsNil)
    51  	c.Assert(ch.URL(), gc.DeepEquals, sch.URL())
    52  	c.Assert(force, gc.Equals, true)
    53  	url, force = s.mysql.CharmURL()
    54  	c.Assert(url, gc.DeepEquals, sch.URL())
    55  	c.Assert(force, gc.Equals, true)
    56  
    57  	// SetCharm fails when the service is Dying.
    58  	_, err = s.mysql.AddUnit()
    59  	c.Assert(err, gc.IsNil)
    60  	err = s.mysql.Destroy()
    61  	c.Assert(err, gc.IsNil)
    62  	err = s.mysql.SetCharm(sch, true)
    63  	c.Assert(err, gc.ErrorMatches, `service "mysql" is not alive`)
    64  }
    65  
    66  func (s *ServiceSuite) TestSetCharmErrors(c *gc.C) {
    67  	logging := s.AddTestingCharm(c, "logging")
    68  	err := s.mysql.SetCharm(logging, false)
    69  	c.Assert(err, gc.ErrorMatches, "cannot change a service's subordinacy")
    70  
    71  	othermysql := s.AddSeriesCharm(c, "mysql", "otherseries")
    72  	err = s.mysql.SetCharm(othermysql, false)
    73  	c.Assert(err, gc.ErrorMatches, "cannot change a service's series")
    74  }
    75  
    76  var metaBase = `
    77  name: mysql
    78  summary: "Fake MySQL Database engine"
    79  description: "Complete with nonsense relations"
    80  provides:
    81    server: mysql
    82  requires:
    83    client: mysql
    84  peers:
    85    cluster: mysql
    86  `
    87  var metaDifferentProvider = `
    88  name: mysql
    89  description: none
    90  summary: none
    91  provides:
    92    kludge: mysql
    93  requires:
    94    client: mysql
    95  peers:
    96    cluster: mysql
    97  `
    98  var metaDifferentRequirer = `
    99  name: mysql
   100  description: none
   101  summary: none
   102  provides:
   103    server: mysql
   104  requires:
   105    kludge: mysql
   106  peers:
   107    cluster: mysql
   108  `
   109  var metaDifferentPeer = `
   110  name: mysql
   111  description: none
   112  summary: none
   113  provides:
   114    server: mysql
   115  requires:
   116    client: mysql
   117  peers:
   118    kludge: mysql
   119  `
   120  var metaExtraEndpoints = `
   121  name: mysql
   122  description: none
   123  summary: none
   124  provides:
   125    server: mysql
   126    foo: bar
   127  requires:
   128    client: mysql
   129    baz: woot
   130  peers:
   131    cluster: mysql
   132    just: me
   133  `
   134  
   135  var setCharmEndpointsTests = []struct {
   136  	summary string
   137  	meta    string
   138  	err     string
   139  }{
   140  	{
   141  		summary: "different provider (but no relation yet)",
   142  		meta:    metaDifferentProvider,
   143  	}, {
   144  		summary: "different requirer (but no relation yet)",
   145  		meta:    metaDifferentRequirer,
   146  	}, {
   147  		summary: "different peer",
   148  		meta:    metaDifferentPeer,
   149  		err:     `cannot upgrade service "fakemysql" to charm "local:quantal/quantal-mysql-5": would break relation "fakemysql:cluster"`,
   150  	}, {
   151  		summary: "same relations ok",
   152  		meta:    metaBase,
   153  	}, {
   154  		summary: "extra endpoints ok",
   155  		meta:    metaExtraEndpoints,
   156  	},
   157  }
   158  
   159  func (s *ServiceSuite) TestSetCharmChecksEndpointsWithoutRelations(c *gc.C) {
   160  	revno := 2 // 1 is used in SetUpSuite
   161  	ms := s.AddMetaCharm(c, "mysql", metaBase, revno)
   162  	svc := s.AddTestingService(c, "fakemysql", ms)
   163  	err := svc.SetCharm(ms, false)
   164  	c.Assert(err, gc.IsNil)
   165  
   166  	for i, t := range setCharmEndpointsTests {
   167  		c.Logf("test %d: %s", i, t.summary)
   168  
   169  		newCh := s.AddMetaCharm(c, "mysql", t.meta, revno+i+1)
   170  		err = svc.SetCharm(newCh, false)
   171  		if t.err != "" {
   172  			c.Assert(err, gc.ErrorMatches, t.err)
   173  		} else {
   174  			c.Assert(err, gc.IsNil)
   175  		}
   176  	}
   177  
   178  	err = svc.Destroy()
   179  	c.Assert(err, gc.IsNil)
   180  }
   181  
   182  func (s *ServiceSuite) TestSetCharmChecksEndpointsWithRelations(c *gc.C) {
   183  	revno := 2 // 1 is used by SetUpSuite
   184  	providerCharm := s.AddMetaCharm(c, "mysql", metaDifferentProvider, revno)
   185  	providerSvc := s.AddTestingService(c, "myprovider", providerCharm)
   186  	err := providerSvc.SetCharm(providerCharm, false)
   187  	c.Assert(err, gc.IsNil)
   188  
   189  	revno++
   190  	requirerCharm := s.AddMetaCharm(c, "mysql", metaDifferentRequirer, revno)
   191  	requirerSvc := s.AddTestingService(c, "myrequirer", requirerCharm)
   192  	err = requirerSvc.SetCharm(requirerCharm, false)
   193  	c.Assert(err, gc.IsNil)
   194  
   195  	eps, err := s.State.InferEndpoints([]string{"myprovider:kludge", "myrequirer:kludge"})
   196  	c.Assert(err, gc.IsNil)
   197  	_, err = s.State.AddRelation(eps...)
   198  	c.Assert(err, gc.IsNil)
   199  
   200  	revno++
   201  	baseCharm := s.AddMetaCharm(c, "mysql", metaBase, revno)
   202  	err = providerSvc.SetCharm(baseCharm, false)
   203  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "myprovider" to charm "local:quantal/quantal-mysql-4": would break relation "myrequirer:kludge myprovider:kludge"`)
   204  	err = requirerSvc.SetCharm(baseCharm, false)
   205  	c.Assert(err, gc.ErrorMatches, `cannot upgrade service "myrequirer" to charm "local:quantal/quantal-mysql-4": would break relation "myrequirer:kludge myprovider:kludge"`)
   206  }
   207  
   208  var stringConfig = `
   209  options:
   210    key: {default: My Key, description: Desc, type: string}
   211  `
   212  var emptyConfig = `
   213  options: {}
   214  `
   215  var floatConfig = `
   216  options:
   217    key: {default: 0.42, description: Float key, type: float}
   218  `
   219  var newStringConfig = `
   220  options:
   221    key: {default: My Key, description: Desc, type: string}
   222    other: {default: None, description: My Other, type: string}
   223  `
   224  
   225  var setCharmConfigTests = []struct {
   226  	summary     string
   227  	startconfig string
   228  	startvalues charm.Settings
   229  	endconfig   string
   230  	endvalues   charm.Settings
   231  	err         string
   232  }{
   233  	{
   234  		summary:     "add float key to empty config",
   235  		startconfig: emptyConfig,
   236  		endconfig:   floatConfig,
   237  	}, {
   238  		summary:     "add string key to empty config",
   239  		startconfig: emptyConfig,
   240  		endconfig:   stringConfig,
   241  	}, {
   242  		summary:     "add string key and preserve existing values",
   243  		startconfig: stringConfig,
   244  		startvalues: charm.Settings{"key": "foo"},
   245  		endconfig:   newStringConfig,
   246  		endvalues:   charm.Settings{"key": "foo"},
   247  	}, {
   248  		summary:     "remove string key",
   249  		startconfig: stringConfig,
   250  		startvalues: charm.Settings{"key": "value"},
   251  		endconfig:   emptyConfig,
   252  	}, {
   253  		summary:     "remove float key",
   254  		startconfig: floatConfig,
   255  		startvalues: charm.Settings{"key": 123.45},
   256  		endconfig:   emptyConfig,
   257  	}, {
   258  		summary:     "change key type without values",
   259  		startconfig: stringConfig,
   260  		endconfig:   floatConfig,
   261  	}, {
   262  		summary:     "change key type with values",
   263  		startconfig: stringConfig,
   264  		startvalues: charm.Settings{"key": "value"},
   265  		endconfig:   floatConfig,
   266  	},
   267  }
   268  
   269  func (s *ServiceSuite) TestSetCharmConfig(c *gc.C) {
   270  	charms := map[string]*state.Charm{
   271  		stringConfig:    s.AddConfigCharm(c, "wordpress", stringConfig, 1),
   272  		emptyConfig:     s.AddConfigCharm(c, "wordpress", emptyConfig, 2),
   273  		floatConfig:     s.AddConfigCharm(c, "wordpress", floatConfig, 3),
   274  		newStringConfig: s.AddConfigCharm(c, "wordpress", newStringConfig, 4),
   275  	}
   276  
   277  	for i, t := range setCharmConfigTests {
   278  		c.Logf("test %d: %s", i, t.summary)
   279  
   280  		origCh := charms[t.startconfig]
   281  		svc := s.AddTestingService(c, "wordpress", origCh)
   282  		err := svc.UpdateConfigSettings(t.startvalues)
   283  		c.Assert(err, gc.IsNil)
   284  
   285  		newCh := charms[t.endconfig]
   286  		err = svc.SetCharm(newCh, false)
   287  		var expectVals charm.Settings
   288  		var expectCh *state.Charm
   289  		if t.err != "" {
   290  			c.Assert(err, gc.ErrorMatches, t.err)
   291  			expectCh = origCh
   292  			expectVals = t.startvalues
   293  		} else {
   294  			c.Assert(err, gc.IsNil)
   295  			expectCh = newCh
   296  			expectVals = t.endvalues
   297  		}
   298  
   299  		sch, _, err := svc.Charm()
   300  		c.Assert(err, gc.IsNil)
   301  		c.Assert(sch.URL(), gc.DeepEquals, expectCh.URL())
   302  		settings, err := svc.ConfigSettings()
   303  		c.Assert(err, gc.IsNil)
   304  		if len(expectVals) == 0 {
   305  			c.Assert(settings, gc.HasLen, 0)
   306  		} else {
   307  			c.Assert(settings, gc.DeepEquals, expectVals)
   308  		}
   309  
   310  		err = svc.Destroy()
   311  		c.Assert(err, gc.IsNil)
   312  	}
   313  }
   314  
   315  var serviceUpdateConfigSettingsTests = []struct {
   316  	about   string
   317  	initial charm.Settings
   318  	update  charm.Settings
   319  	expect  charm.Settings
   320  	err     string
   321  }{{
   322  	about:  "unknown option",
   323  	update: charm.Settings{"foo": "bar"},
   324  	err:    `unknown option "foo"`,
   325  }, {
   326  	about:  "bad type",
   327  	update: charm.Settings{"skill-level": "profound"},
   328  	err:    `option "skill-level" expected int, got "profound"`,
   329  }, {
   330  	about:  "set string",
   331  	update: charm.Settings{"outlook": "positive"},
   332  	expect: charm.Settings{"outlook": "positive"},
   333  }, {
   334  	about:   "unset string and set another",
   335  	initial: charm.Settings{"outlook": "positive"},
   336  	update:  charm.Settings{"outlook": nil, "title": "sir"},
   337  	expect:  charm.Settings{"title": "sir"},
   338  }, {
   339  	about:  "unset missing string",
   340  	update: charm.Settings{"outlook": nil},
   341  }, {
   342  	about:   `empty strings are valid`,
   343  	initial: charm.Settings{"outlook": "positive"},
   344  	update:  charm.Settings{"outlook": "", "title": ""},
   345  	expect:  charm.Settings{"outlook": "", "title": ""},
   346  }, {
   347  	about:   "preserve existing value",
   348  	initial: charm.Settings{"title": "sir"},
   349  	update:  charm.Settings{"username": "admin001"},
   350  	expect:  charm.Settings{"username": "admin001", "title": "sir"},
   351  }, {
   352  	about:   "unset a default value, set a different default",
   353  	initial: charm.Settings{"username": "admin001", "title": "sir"},
   354  	update:  charm.Settings{"username": nil, "title": "My Title"},
   355  	expect:  charm.Settings{"title": "My Title"},
   356  }, {
   357  	about:  "non-string type",
   358  	update: charm.Settings{"skill-level": 303},
   359  	expect: charm.Settings{"skill-level": int64(303)},
   360  }, {
   361  	about:   "unset non-string type",
   362  	initial: charm.Settings{"skill-level": 303},
   363  	update:  charm.Settings{"skill-level": nil},
   364  }}
   365  
   366  func (s *ServiceSuite) TestUpdateConfigSettings(c *gc.C) {
   367  	sch := s.AddTestingCharm(c, "dummy")
   368  	for i, t := range serviceUpdateConfigSettingsTests {
   369  		c.Logf("test %d. %s", i, t.about)
   370  		svc := s.AddTestingService(c, "dummy-service", sch)
   371  		if t.initial != nil {
   372  			err := svc.UpdateConfigSettings(t.initial)
   373  			c.Assert(err, gc.IsNil)
   374  		}
   375  		err := svc.UpdateConfigSettings(t.update)
   376  		if t.err != "" {
   377  			c.Assert(err, gc.ErrorMatches, t.err)
   378  		} else {
   379  			c.Assert(err, gc.IsNil)
   380  			settings, err := svc.ConfigSettings()
   381  			c.Assert(err, gc.IsNil)
   382  			expect := t.expect
   383  			if expect == nil {
   384  				expect = charm.Settings{}
   385  			}
   386  			c.Assert(settings, gc.DeepEquals, expect)
   387  		}
   388  		err = svc.Destroy()
   389  		c.Assert(err, gc.IsNil)
   390  	}
   391  }
   392  
   393  func (s *ServiceSuite) TestSettingsRefCountWorks(c *gc.C) {
   394  	oldCh := s.AddConfigCharm(c, "wordpress", emptyConfig, 1)
   395  	newCh := s.AddConfigCharm(c, "wordpress", emptyConfig, 2)
   396  	svcName := "mywp"
   397  
   398  	assertNoRef := func(sch *state.Charm) {
   399  		_, err := state.ServiceSettingsRefCount(s.State, svcName, sch.URL())
   400  		c.Assert(errgo.Cause(err), gc.Equals, mgo.ErrNotFound)
   401  	}
   402  	assertRef := func(sch *state.Charm, refcount int) {
   403  		rc, err := state.ServiceSettingsRefCount(s.State, svcName, sch.URL())
   404  		c.Assert(err, gc.IsNil)
   405  		c.Assert(rc, gc.Equals, refcount)
   406  	}
   407  
   408  	assertNoRef(oldCh)
   409  	assertNoRef(newCh)
   410  
   411  	svc := s.AddTestingService(c, svcName, oldCh)
   412  	assertRef(oldCh, 1)
   413  	assertNoRef(newCh)
   414  
   415  	err := svc.SetCharm(oldCh, false)
   416  	c.Assert(err, gc.IsNil)
   417  	assertRef(oldCh, 1)
   418  	assertNoRef(newCh)
   419  
   420  	err = svc.SetCharm(newCh, false)
   421  	c.Assert(err, gc.IsNil)
   422  	assertNoRef(oldCh)
   423  	assertRef(newCh, 1)
   424  
   425  	err = svc.SetCharm(oldCh, false)
   426  	c.Assert(err, gc.IsNil)
   427  	assertRef(oldCh, 1)
   428  	assertNoRef(newCh)
   429  
   430  	u, err := svc.AddUnit()
   431  	c.Assert(err, gc.IsNil)
   432  	curl, ok := u.CharmURL()
   433  	c.Assert(ok, gc.Equals, false)
   434  	assertRef(oldCh, 1)
   435  	assertNoRef(newCh)
   436  
   437  	err = u.SetCharmURL(oldCh.URL())
   438  	c.Assert(err, gc.IsNil)
   439  	curl, ok = u.CharmURL()
   440  	c.Assert(ok, gc.Equals, true)
   441  	c.Assert(curl, gc.DeepEquals, oldCh.URL())
   442  	assertRef(oldCh, 2)
   443  	assertNoRef(newCh)
   444  
   445  	err = u.EnsureDead()
   446  	c.Assert(err, gc.IsNil)
   447  	assertRef(oldCh, 2)
   448  	assertNoRef(newCh)
   449  
   450  	err = u.Remove()
   451  	c.Assert(err, gc.IsNil)
   452  	assertRef(oldCh, 1)
   453  	assertNoRef(newCh)
   454  
   455  	err = svc.Destroy()
   456  	c.Assert(err, gc.IsNil)
   457  	assertNoRef(oldCh)
   458  	assertNoRef(newCh)
   459  }
   460  
   461  const mysqlBaseMeta = `
   462  name: mysql
   463  summary: "Database engine"
   464  description: "A pretty popular database"
   465  provides:
   466    server: mysql
   467  `
   468  const onePeerMeta = `
   469  peers:
   470    cluster: mysql
   471  `
   472  const twoPeersMeta = `
   473  peers:
   474    cluster: mysql
   475    loadbalancer: phony
   476  `
   477  
   478  func (s *ServiceSuite) assertServiceRelations(c *gc.C, svc *state.Service, expectedKeys ...string) []*state.Relation {
   479  	rels, err := svc.Relations()
   480  	c.Assert(err, gc.IsNil)
   481  	if len(rels) == 0 {
   482  		return nil
   483  	}
   484  	relKeys := make([]string, len(expectedKeys))
   485  	for i, rel := range rels {
   486  		relKeys[i] = rel.String()
   487  	}
   488  	sort.Strings(relKeys)
   489  	c.Assert(relKeys, gc.DeepEquals, expectedKeys)
   490  	return rels
   491  }
   492  
   493  func (s *ServiceSuite) TestNewPeerRelationsAddedOnUpgrade(c *gc.C) {
   494  	// Original mysql charm has no peer relations.
   495  	oldCh := s.AddMetaCharm(c, "mysql", mysqlBaseMeta+onePeerMeta, 2)
   496  	newCh := s.AddMetaCharm(c, "mysql", mysqlBaseMeta+twoPeersMeta, 3)
   497  
   498  	// No relations joined yet.
   499  	s.assertServiceRelations(c, s.mysql)
   500  
   501  	err := s.mysql.SetCharm(oldCh, false)
   502  	c.Assert(err, gc.IsNil)
   503  	s.assertServiceRelations(c, s.mysql, "mysql:cluster")
   504  
   505  	err = s.mysql.SetCharm(newCh, false)
   506  	c.Assert(err, gc.IsNil)
   507  	rels := s.assertServiceRelations(c, s.mysql, "mysql:cluster", "mysql:loadbalancer")
   508  
   509  	// Check state consistency by attempting to destroy the service.
   510  	err = s.mysql.Destroy()
   511  	c.Assert(err, gc.IsNil)
   512  
   513  	// Check the peer relations got destroyed as well.
   514  	for _, rel := range rels {
   515  		err = rel.Refresh()
   516  		c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   517  	}
   518  }
   519  
   520  func jujuInfoEp(serviceName string) state.Endpoint {
   521  	return state.Endpoint{
   522  		ServiceName: serviceName,
   523  		Relation: charm.Relation{
   524  			Interface: "juju-info",
   525  			Name:      "juju-info",
   526  			Role:      charm.RoleProvider,
   527  			Scope:     charm.ScopeGlobal,
   528  		},
   529  	}
   530  }
   531  
   532  func (s *ServiceSuite) TestTag(c *gc.C) {
   533  	c.Assert(s.mysql.Tag(), gc.Equals, "service-mysql")
   534  }
   535  
   536  func (s *ServiceSuite) TestMysqlEndpoints(c *gc.C) {
   537  	_, err := s.mysql.Endpoint("mysql")
   538  	c.Assert(err, gc.ErrorMatches, `service "mysql" has no "mysql" relation`)
   539  
   540  	jiEP, err := s.mysql.Endpoint("juju-info")
   541  	c.Assert(err, gc.IsNil)
   542  	c.Assert(jiEP, gc.DeepEquals, jujuInfoEp("mysql"))
   543  
   544  	serverEP, err := s.mysql.Endpoint("server")
   545  	c.Assert(err, gc.IsNil)
   546  	c.Assert(serverEP, gc.DeepEquals, state.Endpoint{
   547  		ServiceName: "mysql",
   548  		Relation: charm.Relation{
   549  			Interface: "mysql",
   550  			Name:      "server",
   551  			Role:      charm.RoleProvider,
   552  			Scope:     charm.ScopeGlobal,
   553  		},
   554  	})
   555  
   556  	eps, err := s.mysql.Endpoints()
   557  	c.Assert(err, gc.IsNil)
   558  	c.Assert(eps, gc.DeepEquals, []state.Endpoint{jiEP, serverEP})
   559  }
   560  
   561  func (s *ServiceSuite) TestRiakEndpoints(c *gc.C) {
   562  	riak := s.AddTestingService(c, "myriak", s.AddTestingCharm(c, "riak"))
   563  
   564  	_, err := riak.Endpoint("garble")
   565  	c.Assert(err, gc.ErrorMatches, `service "myriak" has no "garble" relation`)
   566  
   567  	jiEP, err := riak.Endpoint("juju-info")
   568  	c.Assert(err, gc.IsNil)
   569  	c.Assert(jiEP, gc.DeepEquals, jujuInfoEp("myriak"))
   570  
   571  	ringEP, err := riak.Endpoint("ring")
   572  	c.Assert(err, gc.IsNil)
   573  	c.Assert(ringEP, gc.DeepEquals, state.Endpoint{
   574  		ServiceName: "myriak",
   575  		Relation: charm.Relation{
   576  			Interface: "riak",
   577  			Name:      "ring",
   578  			Role:      charm.RolePeer,
   579  			Scope:     charm.ScopeGlobal,
   580  			Limit:     1,
   581  		},
   582  	})
   583  
   584  	adminEP, err := riak.Endpoint("admin")
   585  	c.Assert(err, gc.IsNil)
   586  	c.Assert(adminEP, gc.DeepEquals, state.Endpoint{
   587  		ServiceName: "myriak",
   588  		Relation: charm.Relation{
   589  			Interface: "http",
   590  			Name:      "admin",
   591  			Role:      charm.RoleProvider,
   592  			Scope:     charm.ScopeGlobal,
   593  		},
   594  	})
   595  
   596  	endpointEP, err := riak.Endpoint("endpoint")
   597  	c.Assert(err, gc.IsNil)
   598  	c.Assert(endpointEP, gc.DeepEquals, state.Endpoint{
   599  		ServiceName: "myriak",
   600  		Relation: charm.Relation{
   601  			Interface: "http",
   602  			Name:      "endpoint",
   603  			Role:      charm.RoleProvider,
   604  			Scope:     charm.ScopeGlobal,
   605  		},
   606  	})
   607  
   608  	eps, err := riak.Endpoints()
   609  	c.Assert(err, gc.IsNil)
   610  	c.Assert(eps, gc.DeepEquals, []state.Endpoint{adminEP, endpointEP, jiEP, ringEP})
   611  }
   612  
   613  func (s *ServiceSuite) TestWordpressEndpoints(c *gc.C) {
   614  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   615  
   616  	_, err := wordpress.Endpoint("nonsense")
   617  	c.Assert(err, gc.ErrorMatches, `service "wordpress" has no "nonsense" relation`)
   618  
   619  	jiEP, err := wordpress.Endpoint("juju-info")
   620  	c.Assert(err, gc.IsNil)
   621  	c.Assert(jiEP, gc.DeepEquals, jujuInfoEp("wordpress"))
   622  
   623  	urlEP, err := wordpress.Endpoint("url")
   624  	c.Assert(err, gc.IsNil)
   625  	c.Assert(urlEP, gc.DeepEquals, state.Endpoint{
   626  		ServiceName: "wordpress",
   627  		Relation: charm.Relation{
   628  			Interface: "http",
   629  			Name:      "url",
   630  			Role:      charm.RoleProvider,
   631  			Scope:     charm.ScopeGlobal,
   632  		},
   633  	})
   634  
   635  	ldEP, err := wordpress.Endpoint("logging-dir")
   636  	c.Assert(err, gc.IsNil)
   637  	c.Assert(ldEP, gc.DeepEquals, state.Endpoint{
   638  		ServiceName: "wordpress",
   639  		Relation: charm.Relation{
   640  			Interface: "logging",
   641  			Name:      "logging-dir",
   642  			Role:      charm.RoleProvider,
   643  			Scope:     charm.ScopeContainer,
   644  		},
   645  	})
   646  
   647  	mpEP, err := wordpress.Endpoint("monitoring-port")
   648  	c.Assert(err, gc.IsNil)
   649  	c.Assert(mpEP, gc.DeepEquals, state.Endpoint{
   650  		ServiceName: "wordpress",
   651  		Relation: charm.Relation{
   652  			Interface: "monitoring",
   653  			Name:      "monitoring-port",
   654  			Role:      charm.RoleProvider,
   655  			Scope:     charm.ScopeContainer,
   656  		},
   657  	})
   658  
   659  	dbEP, err := wordpress.Endpoint("db")
   660  	c.Assert(err, gc.IsNil)
   661  	c.Assert(dbEP, gc.DeepEquals, state.Endpoint{
   662  		ServiceName: "wordpress",
   663  		Relation: charm.Relation{
   664  			Interface: "mysql",
   665  			Name:      "db",
   666  			Role:      charm.RoleRequirer,
   667  			Scope:     charm.ScopeGlobal,
   668  			Limit:     1,
   669  		},
   670  	})
   671  
   672  	cacheEP, err := wordpress.Endpoint("cache")
   673  	c.Assert(err, gc.IsNil)
   674  	c.Assert(cacheEP, gc.DeepEquals, state.Endpoint{
   675  		ServiceName: "wordpress",
   676  		Relation: charm.Relation{
   677  			Interface: "varnish",
   678  			Name:      "cache",
   679  			Role:      charm.RoleRequirer,
   680  			Scope:     charm.ScopeGlobal,
   681  			Limit:     2,
   682  			Optional:  true,
   683  		},
   684  	})
   685  
   686  	eps, err := wordpress.Endpoints()
   687  	c.Assert(err, gc.IsNil)
   688  	c.Assert(eps, gc.DeepEquals, []state.Endpoint{cacheEP, dbEP, jiEP, ldEP, mpEP, urlEP})
   689  }
   690  
   691  func (s *ServiceSuite) TestServiceRefresh(c *gc.C) {
   692  	s1, err := s.State.Service(s.mysql.Name())
   693  	c.Assert(err, gc.IsNil)
   694  
   695  	err = s.mysql.SetCharm(s.charm, true)
   696  	c.Assert(err, gc.IsNil)
   697  
   698  	testch, force, err := s1.Charm()
   699  	c.Assert(err, gc.IsNil)
   700  	c.Assert(force, gc.Equals, false)
   701  	c.Assert(testch.URL(), gc.DeepEquals, s.charm.URL())
   702  
   703  	err = s1.Refresh()
   704  	c.Assert(err, gc.IsNil)
   705  	testch, force, err = s1.Charm()
   706  	c.Assert(err, gc.IsNil)
   707  	c.Assert(force, gc.Equals, true)
   708  	c.Assert(testch.URL(), gc.DeepEquals, s.charm.URL())
   709  
   710  	err = s.mysql.Destroy()
   711  	c.Assert(err, gc.IsNil)
   712  	err = s.mysql.Refresh()
   713  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   714  }
   715  
   716  func (s *ServiceSuite) TestServiceExposed(c *gc.C) {
   717  	// Check that querying for the exposed flag works correctly.
   718  	c.Assert(s.mysql.IsExposed(), gc.Equals, false)
   719  
   720  	// Check that setting and clearing the exposed flag works correctly.
   721  	err := s.mysql.SetExposed()
   722  	c.Assert(err, gc.IsNil)
   723  	c.Assert(s.mysql.IsExposed(), gc.Equals, true)
   724  	err = s.mysql.ClearExposed()
   725  	c.Assert(err, gc.IsNil)
   726  	c.Assert(s.mysql.IsExposed(), gc.Equals, false)
   727  
   728  	// Check that setting and clearing the exposed flag repeatedly does not fail.
   729  	err = s.mysql.SetExposed()
   730  	c.Assert(err, gc.IsNil)
   731  	err = s.mysql.SetExposed()
   732  	c.Assert(err, gc.IsNil)
   733  	err = s.mysql.ClearExposed()
   734  	c.Assert(err, gc.IsNil)
   735  	err = s.mysql.ClearExposed()
   736  	c.Assert(err, gc.IsNil)
   737  	err = s.mysql.SetExposed()
   738  	c.Assert(err, gc.IsNil)
   739  	c.Assert(s.mysql.IsExposed(), gc.Equals, true)
   740  
   741  	// Make the service Dying and check that ClearExposed and SetExposed fail.
   742  	// TODO(fwereade): maybe service destruction should always unexpose?
   743  	u, err := s.mysql.AddUnit()
   744  	c.Assert(err, gc.IsNil)
   745  	err = s.mysql.Destroy()
   746  	c.Assert(err, gc.IsNil)
   747  	err = s.mysql.ClearExposed()
   748  	c.Assert(err, gc.ErrorMatches, notAliveErr)
   749  	err = s.mysql.SetExposed()
   750  	c.Assert(err, gc.ErrorMatches, notAliveErr)
   751  
   752  	// Remove the service and check that both fail.
   753  	err = u.EnsureDead()
   754  	c.Assert(err, gc.IsNil)
   755  	err = u.Remove()
   756  	c.Assert(err, gc.IsNil)
   757  	err = s.mysql.SetExposed()
   758  	c.Assert(err, gc.ErrorMatches, notAliveErr)
   759  	err = s.mysql.ClearExposed()
   760  	c.Assert(err, gc.ErrorMatches, notAliveErr)
   761  }
   762  
   763  func (s *ServiceSuite) TestAddUnit(c *gc.C) {
   764  	// Check that principal units can be added on their own.
   765  	unitZero, err := s.mysql.AddUnit()
   766  	c.Assert(err, gc.IsNil)
   767  	c.Assert(unitZero.Name(), gc.Equals, "mysql/0")
   768  	c.Assert(unitZero.IsPrincipal(), gc.Equals, true)
   769  	c.Assert(unitZero.SubordinateNames(), gc.HasLen, 0)
   770  	unitOne, err := s.mysql.AddUnit()
   771  	c.Assert(err, gc.IsNil)
   772  	c.Assert(unitOne.Name(), gc.Equals, "mysql/1")
   773  	c.Assert(unitOne.IsPrincipal(), gc.Equals, true)
   774  	c.Assert(unitOne.SubordinateNames(), gc.HasLen, 0)
   775  
   776  	// Assign the principal unit to a machine.
   777  	m, err := s.State.AddMachine("quantal", state.JobHostUnits)
   778  	c.Assert(err, gc.IsNil)
   779  	err = unitZero.AssignToMachine(m)
   780  	c.Assert(err, gc.IsNil)
   781  
   782  	// Add a subordinate service and check that units cannot be added directly.
   783  	// to add a subordinate unit.
   784  	subCharm := s.AddTestingCharm(c, "logging")
   785  	logging := s.AddTestingService(c, "logging", subCharm)
   786  	_, err = logging.AddUnit()
   787  	c.Assert(err, gc.ErrorMatches, `cannot add unit to service "logging": service is a subordinate`)
   788  
   789  	// Indirectly create a subordinate unit by adding a relation and entering
   790  	// scope as a principal.
   791  	eps, err := s.State.InferEndpoints([]string{"logging", "mysql"})
   792  	c.Assert(err, gc.IsNil)
   793  	rel, err := s.State.AddRelation(eps...)
   794  	c.Assert(err, gc.IsNil)
   795  	ru, err := rel.Unit(unitZero)
   796  	c.Assert(err, gc.IsNil)
   797  	err = ru.EnterScope(nil)
   798  	c.Assert(err, gc.IsNil)
   799  	subZero, err := s.State.Unit("logging/0")
   800  	c.Assert(err, gc.IsNil)
   801  
   802  	// Check that once it's refreshed unitZero has subordinates.
   803  	err = unitZero.Refresh()
   804  	c.Assert(err, gc.IsNil)
   805  	c.Assert(unitZero.SubordinateNames(), gc.DeepEquals, []string{"logging/0"})
   806  
   807  	// Check the subordinate unit has been assigned its principal's machine.
   808  	id, err := subZero.AssignedMachineId()
   809  	c.Assert(err, gc.IsNil)
   810  	c.Assert(id, gc.Equals, m.Id())
   811  }
   812  
   813  func (s *ServiceSuite) TestAddUnitWhenNotAlive(c *gc.C) {
   814  	u, err := s.mysql.AddUnit()
   815  	c.Assert(err, gc.IsNil)
   816  	err = s.mysql.Destroy()
   817  	c.Assert(err, gc.IsNil)
   818  	_, err = s.mysql.AddUnit()
   819  	c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql": service is not alive`)
   820  	err = u.EnsureDead()
   821  	c.Assert(err, gc.IsNil)
   822  	err = u.Remove()
   823  	c.Assert(err, gc.IsNil)
   824  	_, err = s.mysql.AddUnit()
   825  	c.Assert(err, gc.ErrorMatches, `cannot add unit to service "mysql": service "mysql" not found`)
   826  }
   827  
   828  func (s *ServiceSuite) TestReadUnit(c *gc.C) {
   829  	_, err := s.mysql.AddUnit()
   830  	c.Assert(err, gc.IsNil)
   831  	_, err = s.mysql.AddUnit()
   832  	c.Assert(err, gc.IsNil)
   833  
   834  	// Check that retrieving a unit from the service works correctly.
   835  	unit, err := s.mysql.Unit("mysql/0")
   836  	c.Assert(err, gc.IsNil)
   837  	c.Assert(unit.Name(), gc.Equals, "mysql/0")
   838  
   839  	// Check that retrieving a unit from state works correctly.
   840  	unit, err = s.State.Unit("mysql/0")
   841  	c.Assert(err, gc.IsNil)
   842  	c.Assert(unit.Name(), gc.Equals, "mysql/0")
   843  
   844  	// Check that retrieving a non-existent or an invalidly
   845  	// named unit fail nicely.
   846  	unit, err = s.mysql.Unit("mysql")
   847  	c.Assert(err, gc.ErrorMatches, `"mysql" is not a valid unit name`)
   848  	unit, err = s.mysql.Unit("mysql/0/0")
   849  	c.Assert(err, gc.ErrorMatches, `"mysql/0/0" is not a valid unit name`)
   850  	unit, err = s.mysql.Unit("pressword/0")
   851  	c.Assert(err, gc.ErrorMatches, `cannot get unit "pressword/0" from service "mysql": .*`)
   852  
   853  	// Check direct state retrieval also fails nicely.
   854  	unit, err = s.State.Unit("mysql")
   855  	c.Assert(err, gc.ErrorMatches, `"mysql" is not a valid unit name`)
   856  	unit, err = s.State.Unit("mysql/0/0")
   857  	c.Assert(err, gc.ErrorMatches, `"mysql/0/0" is not a valid unit name`)
   858  	unit, err = s.State.Unit("pressword/0")
   859  	c.Assert(err, gc.ErrorMatches, `unit "pressword/0" not found`)
   860  
   861  	// Add another service to check units are not misattributed.
   862  	mysql := s.AddTestingService(c, "wordpress", s.charm)
   863  	_, err = mysql.AddUnit()
   864  	c.Assert(err, gc.IsNil)
   865  
   866  	// BUG(aram): use error strings from state.
   867  	unit, err = s.mysql.Unit("wordpress/0")
   868  	c.Assert(err, gc.ErrorMatches, `cannot get unit "wordpress/0" from service "mysql": .*`)
   869  
   870  	units, err := s.mysql.AllUnits()
   871  	c.Assert(err, gc.IsNil)
   872  	c.Assert(sortedUnitNames(units), gc.DeepEquals, []string{"mysql/0", "mysql/1"})
   873  }
   874  
   875  func (s *ServiceSuite) TestReadUnitWhenDying(c *gc.C) {
   876  	// Test that we can still read units when the service is Dying...
   877  	unit, err := s.mysql.AddUnit()
   878  	c.Assert(err, gc.IsNil)
   879  	preventUnitDestroyRemove(c, unit)
   880  	err = s.mysql.Destroy()
   881  	c.Assert(err, gc.IsNil)
   882  	_, err = s.mysql.AllUnits()
   883  	c.Assert(err, gc.IsNil)
   884  	_, err = s.mysql.Unit("mysql/0")
   885  	c.Assert(err, gc.IsNil)
   886  
   887  	// ...and when those units are Dying or Dead...
   888  	testWhenDying(c, unit, noErr, noErr, func() error {
   889  		_, err := s.mysql.AllUnits()
   890  		return err
   891  	}, func() error {
   892  		_, err := s.mysql.Unit("mysql/0")
   893  		return err
   894  	})
   895  
   896  	// ...and even, in a very limited way, when the service itself is removed.
   897  	removeAllUnits(c, s.mysql)
   898  	_, err = s.mysql.AllUnits()
   899  	c.Assert(err, gc.IsNil)
   900  }
   901  
   902  func (s *ServiceSuite) TestDestroySimple(c *gc.C) {
   903  	err := s.mysql.Destroy()
   904  	c.Assert(err, gc.IsNil)
   905  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
   906  	err = s.mysql.Refresh()
   907  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   908  }
   909  
   910  func (s *ServiceSuite) TestDestroyStillHasUnits(c *gc.C) {
   911  	unit, err := s.mysql.AddUnit()
   912  	c.Assert(err, gc.IsNil)
   913  	err = s.mysql.Destroy()
   914  	c.Assert(err, gc.IsNil)
   915  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
   916  
   917  	err = unit.EnsureDead()
   918  	c.Assert(err, gc.IsNil)
   919  	err = s.mysql.Refresh()
   920  	c.Assert(err, gc.IsNil)
   921  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
   922  
   923  	err = unit.Remove()
   924  	c.Assert(err, gc.IsNil)
   925  	err = s.mysql.Refresh()
   926  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   927  }
   928  
   929  func (s *ServiceSuite) TestDestroyOnceHadUnits(c *gc.C) {
   930  	unit, err := s.mysql.AddUnit()
   931  	c.Assert(err, gc.IsNil)
   932  	err = unit.EnsureDead()
   933  	c.Assert(err, gc.IsNil)
   934  	err = unit.Remove()
   935  	c.Assert(err, gc.IsNil)
   936  
   937  	err = s.mysql.Destroy()
   938  	c.Assert(err, gc.IsNil)
   939  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
   940  	err = s.mysql.Refresh()
   941  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   942  }
   943  
   944  func (s *ServiceSuite) TestDestroyStaleNonZeroUnitCount(c *gc.C) {
   945  	unit, err := s.mysql.AddUnit()
   946  	c.Assert(err, gc.IsNil)
   947  	err = s.mysql.Refresh()
   948  	c.Assert(err, gc.IsNil)
   949  	err = unit.EnsureDead()
   950  	c.Assert(err, gc.IsNil)
   951  	err = unit.Remove()
   952  	c.Assert(err, gc.IsNil)
   953  
   954  	err = s.mysql.Destroy()
   955  	c.Assert(err, gc.IsNil)
   956  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
   957  	err = s.mysql.Refresh()
   958  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   959  }
   960  
   961  func (s *ServiceSuite) TestDestroyStaleZeroUnitCount(c *gc.C) {
   962  	unit, err := s.mysql.AddUnit()
   963  	c.Assert(err, gc.IsNil)
   964  
   965  	err = s.mysql.Destroy()
   966  	c.Assert(err, gc.IsNil)
   967  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
   968  
   969  	err = s.mysql.Refresh()
   970  	c.Assert(err, gc.IsNil)
   971  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
   972  
   973  	err = unit.EnsureDead()
   974  	c.Assert(err, gc.IsNil)
   975  	err = s.mysql.Refresh()
   976  	c.Assert(err, gc.IsNil)
   977  	c.Assert(s.mysql.Life(), gc.Equals, state.Dying)
   978  
   979  	err = unit.Remove()
   980  	c.Assert(err, gc.IsNil)
   981  	err = s.mysql.Refresh()
   982  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   983  }
   984  
   985  func (s *ServiceSuite) TestDestroyWithRemovableRelation(c *gc.C) {
   986  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   987  	eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"})
   988  	c.Assert(err, gc.IsNil)
   989  	rel, err := s.State.AddRelation(eps...)
   990  	c.Assert(err, gc.IsNil)
   991  
   992  	// Destroy a service with no units in relation scope; check service and
   993  	// unit removed.
   994  	err = wordpress.Destroy()
   995  	c.Assert(err, gc.IsNil)
   996  	err = wordpress.Refresh()
   997  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   998  	err = rel.Refresh()
   999  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
  1000  }
  1001  
  1002  func (s *ServiceSuite) TestDestroyWithReferencedRelation(c *gc.C) {
  1003  	s.assertDestroyWithReferencedRelation(c, true)
  1004  }
  1005  
  1006  func (s *ServiceSuite) TestDestroyWithreferencedRelationStaleCount(c *gc.C) {
  1007  	s.assertDestroyWithReferencedRelation(c, false)
  1008  }
  1009  
  1010  func (s *ServiceSuite) assertDestroyWithReferencedRelation(c *gc.C, refresh bool) {
  1011  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
  1012  	eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"})
  1013  	c.Assert(err, gc.IsNil)
  1014  	rel0, err := s.State.AddRelation(eps...)
  1015  	c.Assert(err, gc.IsNil)
  1016  
  1017  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
  1018  	eps, err = s.State.InferEndpoints([]string{"logging", "mysql"})
  1019  	c.Assert(err, gc.IsNil)
  1020  	rel1, err := s.State.AddRelation(eps...)
  1021  	c.Assert(err, gc.IsNil)
  1022  
  1023  	// Add a separate reference to the first relation.
  1024  	unit, err := wordpress.AddUnit()
  1025  	c.Assert(err, gc.IsNil)
  1026  	ru, err := rel0.Unit(unit)
  1027  	c.Assert(err, gc.IsNil)
  1028  	err = ru.EnterScope(nil)
  1029  	c.Assert(err, gc.IsNil)
  1030  
  1031  	// Optionally update the service document to get correct relation counts.
  1032  	if refresh {
  1033  		err = s.mysql.Destroy()
  1034  		c.Assert(err, gc.IsNil)
  1035  	}
  1036  
  1037  	// Destroy, and check that the first relation becomes Dying...
  1038  	err = s.mysql.Destroy()
  1039  	c.Assert(err, gc.IsNil)
  1040  	err = rel0.Refresh()
  1041  	c.Assert(err, gc.IsNil)
  1042  	c.Assert(rel0.Life(), gc.Equals, state.Dying)
  1043  
  1044  	// ...while the second is removed directly.
  1045  	err = rel1.Refresh()
  1046  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
  1047  
  1048  	// Drop the last reference to the first relation; check the relation and
  1049  	// the service are are both removed.
  1050  	err = ru.LeaveScope()
  1051  	c.Assert(err, gc.IsNil)
  1052  	err = s.mysql.Refresh()
  1053  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
  1054  	err = rel0.Refresh()
  1055  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
  1056  }
  1057  
  1058  func (s *ServiceSuite) TestDestroyQueuesUnitCleanup(c *gc.C) {
  1059  	// Add 5 units; block quick-remove of mysql/1 and mysql/3
  1060  	units := make([]*state.Unit, 5)
  1061  	for i := range units {
  1062  		unit, err := s.mysql.AddUnit()
  1063  		c.Assert(err, gc.IsNil)
  1064  		units[i] = unit
  1065  		if i%2 != 0 {
  1066  			preventUnitDestroyRemove(c, unit)
  1067  		}
  1068  	}
  1069  
  1070  	// Check state is clean.
  1071  	dirty, err := s.State.NeedsCleanup()
  1072  	c.Assert(err, gc.IsNil)
  1073  	c.Assert(dirty, gc.Equals, false)
  1074  
  1075  	// Destroy mysql, and check units are not touched.
  1076  	err = s.mysql.Destroy()
  1077  	c.Assert(err, gc.IsNil)
  1078  	for _, unit := range units {
  1079  		assertLife(c, unit, state.Alive)
  1080  	}
  1081  
  1082  	// Check a cleanup doc was added.
  1083  	dirty, err = s.State.NeedsCleanup()
  1084  	c.Assert(err, gc.IsNil)
  1085  	c.Assert(dirty, gc.Equals, true)
  1086  
  1087  	// Run the cleanup and check the units.
  1088  	err = s.State.Cleanup()
  1089  	c.Assert(err, gc.IsNil)
  1090  	for i, unit := range units {
  1091  		if i%2 != 0 {
  1092  			assertLife(c, unit, state.Dying)
  1093  		} else {
  1094  			assertRemoved(c, unit)
  1095  		}
  1096  	}
  1097  
  1098  	// Check we're now clean.
  1099  	dirty, err = s.State.NeedsCleanup()
  1100  	c.Assert(err, gc.IsNil)
  1101  	c.Assert(dirty, gc.Equals, false)
  1102  }
  1103  
  1104  func (s *ServiceSuite) TestReadUnitWithChangingState(c *gc.C) {
  1105  	// Check that reading a unit after removing the service
  1106  	// fails nicely.
  1107  	err := s.mysql.Destroy()
  1108  	c.Assert(err, gc.IsNil)
  1109  	err = s.mysql.Refresh()
  1110  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
  1111  	_, err = s.State.Unit("mysql/0")
  1112  	c.Assert(err, gc.ErrorMatches, `unit "mysql/0" not found`)
  1113  }
  1114  
  1115  func uint64p(val uint64) *uint64 {
  1116  	return &val
  1117  }
  1118  
  1119  func (s *ServiceSuite) TestConstraints(c *gc.C) {
  1120  	// Constraints are initially empty (for now).
  1121  	cons, err := s.mysql.Constraints()
  1122  	c.Assert(err, gc.IsNil)
  1123  	c.Assert(&cons, jc.Satisfies, constraints.IsEmpty)
  1124  
  1125  	// Constraints can be set.
  1126  	cons2 := constraints.Value{Mem: uint64p(4096)}
  1127  	err = s.mysql.SetConstraints(cons2)
  1128  	cons3, err := s.mysql.Constraints()
  1129  	c.Assert(err, gc.IsNil)
  1130  	c.Assert(cons3, gc.DeepEquals, cons2)
  1131  
  1132  	// Constraints are completely overwritten when re-set.
  1133  	cons4 := constraints.Value{CpuPower: uint64p(750)}
  1134  	err = s.mysql.SetConstraints(cons4)
  1135  	c.Assert(err, gc.IsNil)
  1136  	cons5, err := s.mysql.Constraints()
  1137  	c.Assert(err, gc.IsNil)
  1138  	c.Assert(cons5, gc.DeepEquals, cons4)
  1139  
  1140  	// Destroy the existing service; there's no way to directly assert
  1141  	// that the constraints are deleted...
  1142  	err = s.mysql.Destroy()
  1143  	c.Assert(err, gc.IsNil)
  1144  	err = s.mysql.Refresh()
  1145  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
  1146  
  1147  	// ...but we can check that old constraints do not affect new services
  1148  	// with matching names.
  1149  	ch, _, err := s.mysql.Charm()
  1150  	c.Assert(err, gc.IsNil)
  1151  	mysql := s.AddTestingService(c, s.mysql.Name(), ch)
  1152  	cons6, err := mysql.Constraints()
  1153  	c.Assert(err, gc.IsNil)
  1154  	c.Assert(&cons6, jc.Satisfies, constraints.IsEmpty)
  1155  }
  1156  
  1157  func (s *ServiceSuite) TestConstraintsLifecycle(c *gc.C) {
  1158  	// Dying.
  1159  	unit, err := s.mysql.AddUnit()
  1160  	c.Assert(err, gc.IsNil)
  1161  	err = s.mysql.Destroy()
  1162  	c.Assert(err, gc.IsNil)
  1163  	cons1 := constraints.MustParse("mem=1G")
  1164  	err = s.mysql.SetConstraints(cons1)
  1165  	c.Assert(err, gc.ErrorMatches, `cannot set constraints: not found or not alive`)
  1166  	scons, err := s.mysql.Constraints()
  1167  	c.Assert(err, gc.IsNil)
  1168  	c.Assert(&scons, jc.Satisfies, constraints.IsEmpty)
  1169  
  1170  	// Removed (== Dead, for a service).
  1171  	err = unit.EnsureDead()
  1172  	c.Assert(err, gc.IsNil)
  1173  	err = unit.Remove()
  1174  	c.Assert(err, gc.IsNil)
  1175  	err = s.mysql.SetConstraints(cons1)
  1176  	c.Assert(err, gc.ErrorMatches, `cannot set constraints: not found or not alive`)
  1177  	_, err = s.mysql.Constraints()
  1178  	c.Assert(err, gc.ErrorMatches, `constraints not found`)
  1179  }
  1180  
  1181  func (s *ServiceSuite) TestSubordinateConstraints(c *gc.C) {
  1182  	loggingCh := s.AddTestingCharm(c, "logging")
  1183  	logging := s.AddTestingService(c, "logging", loggingCh)
  1184  
  1185  	_, err := logging.Constraints()
  1186  	c.Assert(errgo.Cause(err), gc.Equals, state.ErrSubordinateConstraints)
  1187  
  1188  	err = logging.SetConstraints(constraints.Value{})
  1189  	c.Assert(errgo.Cause(err), gc.Equals, state.ErrSubordinateConstraints)
  1190  }
  1191  
  1192  func (s *ServiceSuite) TestWatchUnitsBulkEvents(c *gc.C) {
  1193  	// Alive unit...
  1194  	alive, err := s.mysql.AddUnit()
  1195  	c.Assert(err, gc.IsNil)
  1196  
  1197  	// Dying unit...
  1198  	dying, err := s.mysql.AddUnit()
  1199  	c.Assert(err, gc.IsNil)
  1200  	preventUnitDestroyRemove(c, dying)
  1201  	err = dying.Destroy()
  1202  	c.Assert(err, gc.IsNil)
  1203  
  1204  	// Dead unit...
  1205  	dead, err := s.mysql.AddUnit()
  1206  	c.Assert(err, gc.IsNil)
  1207  	preventUnitDestroyRemove(c, dead)
  1208  	err = dead.Destroy()
  1209  	c.Assert(err, gc.IsNil)
  1210  	err = dead.EnsureDead()
  1211  	c.Assert(err, gc.IsNil)
  1212  
  1213  	// Gone unit.
  1214  	gone, err := s.mysql.AddUnit()
  1215  	c.Assert(err, gc.IsNil)
  1216  	err = gone.Destroy()
  1217  	c.Assert(err, gc.IsNil)
  1218  
  1219  	// All except gone unit are reported in initial event.
  1220  	w := s.mysql.WatchUnits()
  1221  	defer testing.AssertStop(c, w)
  1222  	wc := testing.NewStringsWatcherC(c, s.State, w)
  1223  	wc.AssertChange(alive.Name(), dying.Name(), dead.Name())
  1224  	wc.AssertNoChange()
  1225  
  1226  	// Remove them all; alive/dying changes reported; dead never mentioned again.
  1227  	err = alive.Destroy()
  1228  	c.Assert(err, gc.IsNil)
  1229  	err = dying.EnsureDead()
  1230  	c.Assert(err, gc.IsNil)
  1231  	err = dying.Remove()
  1232  	c.Assert(err, gc.IsNil)
  1233  	err = dead.Remove()
  1234  	c.Assert(err, gc.IsNil)
  1235  	wc.AssertChange(alive.Name(), dying.Name())
  1236  	wc.AssertNoChange()
  1237  }
  1238  
  1239  func (s *ServiceSuite) TestWatchUnitsLifecycle(c *gc.C) {
  1240  	// Empty initial event when no units.
  1241  	w := s.mysql.WatchUnits()
  1242  	defer testing.AssertStop(c, w)
  1243  	wc := testing.NewStringsWatcherC(c, s.State, w)
  1244  	wc.AssertChange()
  1245  	wc.AssertNoChange()
  1246  
  1247  	// Create one unit, check one change.
  1248  	quick, err := s.mysql.AddUnit()
  1249  	c.Assert(err, gc.IsNil)
  1250  	wc.AssertChange(quick.Name())
  1251  	wc.AssertNoChange()
  1252  
  1253  	// Destroy that unit (short-circuited to removal), check one change.
  1254  	err = quick.Destroy()
  1255  	c.Assert(err, gc.IsNil)
  1256  	wc.AssertChange(quick.Name())
  1257  	wc.AssertNoChange()
  1258  
  1259  	// Create another, check one change.
  1260  	slow, err := s.mysql.AddUnit()
  1261  	c.Assert(err, gc.IsNil)
  1262  	wc.AssertChange(slow.Name())
  1263  	wc.AssertNoChange()
  1264  
  1265  	// Change unit itself, no change.
  1266  	preventUnitDestroyRemove(c, slow)
  1267  	wc.AssertNoChange()
  1268  
  1269  	// Make unit Dying, change detected.
  1270  	err = slow.Destroy()
  1271  	c.Assert(err, gc.IsNil)
  1272  	wc.AssertChange(slow.Name())
  1273  	wc.AssertNoChange()
  1274  
  1275  	// Make unit Dead, change detected.
  1276  	err = slow.EnsureDead()
  1277  	c.Assert(err, gc.IsNil)
  1278  	wc.AssertChange(slow.Name())
  1279  	wc.AssertNoChange()
  1280  
  1281  	// Remove unit, final change not detected.
  1282  	err = slow.Remove()
  1283  	c.Assert(err, gc.IsNil)
  1284  	wc.AssertNoChange()
  1285  }
  1286  
  1287  func (s *ServiceSuite) TestWatchRelations(c *gc.C) {
  1288  	// TODO(fwereade) split this test up a bit.
  1289  	w := s.mysql.WatchRelations()
  1290  	defer testing.AssertStop(c, w)
  1291  	wc := testing.NewStringsWatcherC(c, s.State, w)
  1292  	wc.AssertChange()
  1293  	wc.AssertNoChange()
  1294  
  1295  	// Add a relation; check change.
  1296  	mysqlep, err := s.mysql.Endpoint("server")
  1297  	c.Assert(err, gc.IsNil)
  1298  	wpch := s.AddTestingCharm(c, "wordpress")
  1299  	wpi := 0
  1300  	addRelation := func() *state.Relation {
  1301  		name := fmt.Sprintf("wp%d", wpi)
  1302  		wpi++
  1303  		wp := s.AddTestingService(c, name, wpch)
  1304  		wpep, err := wp.Endpoint("db")
  1305  		c.Assert(err, gc.IsNil)
  1306  		rel, err := s.State.AddRelation(mysqlep, wpep)
  1307  		c.Assert(err, gc.IsNil)
  1308  		return rel
  1309  	}
  1310  	rel0 := addRelation()
  1311  	wc.AssertChange(rel0.String())
  1312  	wc.AssertNoChange()
  1313  
  1314  	// Add another relation; check change.
  1315  	rel1 := addRelation()
  1316  	wc.AssertChange(rel1.String())
  1317  	wc.AssertNoChange()
  1318  
  1319  	// Destroy a relation; check change.
  1320  	err = rel0.Destroy()
  1321  	c.Assert(err, gc.IsNil)
  1322  	wc.AssertChange(rel0.String())
  1323  	wc.AssertNoChange()
  1324  
  1325  	// Stop watcher; check change chan is closed.
  1326  	testing.AssertStop(c, w)
  1327  	wc.AssertClosed()
  1328  
  1329  	// Add a new relation; start a new watcher; check initial event.
  1330  	rel2 := addRelation()
  1331  	w = s.mysql.WatchRelations()
  1332  	defer testing.AssertStop(c, w)
  1333  	wc = testing.NewStringsWatcherC(c, s.State, w)
  1334  	wc.AssertChange(rel1.String(), rel2.String())
  1335  	wc.AssertNoChange()
  1336  
  1337  	// Add a unit to the new relation; check no change.
  1338  	unit, err := s.mysql.AddUnit()
  1339  	c.Assert(err, gc.IsNil)
  1340  	ru2, err := rel2.Unit(unit)
  1341  	c.Assert(err, gc.IsNil)
  1342  	err = ru2.EnterScope(nil)
  1343  	c.Assert(err, gc.IsNil)
  1344  	wc.AssertNoChange()
  1345  
  1346  	// Destroy the relation with the unit in scope, and add another; check
  1347  	// changes.
  1348  	err = rel2.Destroy()
  1349  	c.Assert(err, gc.IsNil)
  1350  	rel3 := addRelation()
  1351  	wc.AssertChange(rel2.String(), rel3.String())
  1352  	wc.AssertNoChange()
  1353  
  1354  	// Leave scope, destroying the relation, and check that change as well.
  1355  	err = ru2.LeaveScope()
  1356  	c.Assert(err, gc.IsNil)
  1357  	wc.AssertChange(rel2.String())
  1358  	wc.AssertNoChange()
  1359  }
  1360  
  1361  func removeAllUnits(c *gc.C, s *state.Service) {
  1362  	us, err := s.AllUnits()
  1363  	c.Assert(err, gc.IsNil)
  1364  	for _, u := range us {
  1365  		err = u.EnsureDead()
  1366  		c.Assert(err, gc.IsNil)
  1367  		err = u.Remove()
  1368  		c.Assert(err, gc.IsNil)
  1369  	}
  1370  }
  1371  
  1372  func (s *ServiceSuite) TestWatchService(c *gc.C) {
  1373  	w := s.mysql.Watch()
  1374  	defer testing.AssertStop(c, w)
  1375  
  1376  	// Initial event.
  1377  	wc := testing.NewNotifyWatcherC(c, s.State, w)
  1378  	wc.AssertOneChange()
  1379  
  1380  	// Make one change (to a separate instance), check one event.
  1381  	service, err := s.State.Service(s.mysql.Name())
  1382  	c.Assert(err, gc.IsNil)
  1383  	err = service.SetExposed()
  1384  	c.Assert(err, gc.IsNil)
  1385  	wc.AssertOneChange()
  1386  
  1387  	// Make two changes, check one event.
  1388  	err = service.ClearExposed()
  1389  	c.Assert(err, gc.IsNil)
  1390  	err = service.SetCharm(s.charm, true)
  1391  	c.Assert(err, gc.IsNil)
  1392  	wc.AssertOneChange()
  1393  
  1394  	// Stop, check closed.
  1395  	testing.AssertStop(c, w)
  1396  	wc.AssertClosed()
  1397  
  1398  	// Remove service, start new watch, check single event.
  1399  	err = service.Destroy()
  1400  	c.Assert(err, gc.IsNil)
  1401  	w = s.mysql.Watch()
  1402  	defer testing.AssertStop(c, w)
  1403  	testing.NewNotifyWatcherC(c, s.State, w).AssertOneChange()
  1404  }
  1405  
  1406  func (s *ServiceSuite) TestAnnotatorForService(c *gc.C) {
  1407  	testAnnotator(c, func() (state.Annotator, error) {
  1408  		return s.State.Service("mysql")
  1409  	})
  1410  }
  1411  
  1412  func (s *ServiceSuite) TestAnnotationRemovalForService(c *gc.C) {
  1413  	annotations := map[string]string{"mykey": "myvalue"}
  1414  	err := s.mysql.SetAnnotations(annotations)
  1415  	c.Assert(err, gc.IsNil)
  1416  	err = s.mysql.Destroy()
  1417  	c.Assert(err, gc.IsNil)
  1418  	ann, err := s.mysql.Annotations()
  1419  	c.Assert(err, gc.IsNil)
  1420  	c.Assert(ann, gc.DeepEquals, make(map[string]string))
  1421  }
  1422  
  1423  // SCHEMACHANGE
  1424  // TODO(mattyw) remove when schema upgrades are possible
  1425  // Check that GetOwnerTag returns user-admin even
  1426  // when the service has no owner
  1427  func (s *ServiceSuite) TestOwnerTagSchemaProtection(c *gc.C) {
  1428  	service := s.AddTestingService(c, "foobar", s.charm)
  1429  	state.SetServiceOwnerTag(service, "")
  1430  	c.Assert(state.GetServiceOwnerTag(service), gc.Equals, "")
  1431  	c.Assert(service.GetOwnerTag(), gc.Equals, "user-admin")
  1432  }