1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
13
"github.com/juju/errors"
14
jc "github.com/juju/testing/checkers"
15
gc "gopkg.in/check.v1"
17
"github.com/juju/juju/agent"
18
"github.com/juju/juju/api"
19
apideployer "github.com/juju/juju/api/deployer"
20
jujutesting "github.com/juju/juju/juju/testing"
21
"github.com/juju/juju/state"
22
coretesting "github.com/juju/juju/testing"
23
"github.com/juju/juju/worker"
24
"github.com/juju/juju/worker/deployer"
27
func TestPackage(t *stdtesting.T) {
28
//TODO(bogdanteleaga): Fix this on windows
29
if runtime.GOOS == "windows" {
30
t.Skip("bug 1403084: Currently does not work under windows")
32
coretesting.MgoTestPackage(t)
35
type deployerSuite struct {
36
jujutesting.JujuConnSuite
39
machine *state.Machine
40
stateAPI api.Connection
41
deployerState *apideployer.State
44
var _ = gc.Suite(&deployerSuite{})
46
var _ worker.StringsWatchHandler = (*deployer.Deployer)(nil)
48
func (s *deployerSuite) SetUpTest(c *gc.C) {
49
s.JujuConnSuite.SetUpTest(c)
50
s.SimpleToolsFixture.SetUp(c, s.DataDir())
51
s.stateAPI, s.machine = s.OpenAPIAsNewMachine(c)
52
// Create the deployer facade.
53
s.deployerState = s.stateAPI.Deployer()
54
c.Assert(s.deployerState, gc.NotNil)
57
func (s *deployerSuite) TearDownTest(c *gc.C) {
58
s.SimpleToolsFixture.TearDown(c)
59
s.JujuConnSuite.TearDownTest(c)
62
func (s *deployerSuite) makeDeployerAndContext(c *gc.C) (worker.Worker, deployer.Context) {
63
// Create a deployer acting on behalf of the machine.
64
ctx := s.getContextForMachine(c, s.machine.Tag())
65
return deployer.NewDeployer(s.deployerState, ctx), ctx
68
func (s *deployerSuite) TestDeployRecallRemovePrincipals(c *gc.C) {
69
// Create a machine, and a couple of units.
70
svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
71
u0, err := svc.AddUnit()
72
c.Assert(err, jc.ErrorIsNil)
73
u1, err := svc.AddUnit()
74
c.Assert(err, jc.ErrorIsNil)
76
dep, ctx := s.makeDeployerAndContext(c)
79
// Assign one unit, and wait for it to be deployed.
80
err = u0.AssignToMachine(s.machine)
81
c.Assert(err, jc.ErrorIsNil)
82
s.waitFor(c, isDeployed(ctx, u0.Name()))
84
// Assign another unit, and wait for that to be deployed.
85
err = u1.AssignToMachine(s.machine)
86
c.Assert(err, jc.ErrorIsNil)
87
s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name()))
89
// Cause a unit to become Dying, and check no change.
90
err = u1.SetAgentStatus(state.StatusIdle, "", nil)
91
c.Assert(err, jc.ErrorIsNil)
93
c.Assert(err, jc.ErrorIsNil)
94
s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name()))
96
// Cause a unit to become Dead, and check that it is both recalled and
97
// removed from state.
99
c.Assert(err, jc.ErrorIsNil)
100
s.waitFor(c, isRemoved(s.State, u0.Name()))
101
s.waitFor(c, isDeployed(ctx, u1.Name()))
103
// Remove the Dying unit from the machine, and check that it is recalled...
104
err = u1.UnassignFromMachine()
105
c.Assert(err, jc.ErrorIsNil)
106
s.waitFor(c, isDeployed(ctx))
108
// ...and that the deployer, no longer bearing any responsibility for the
109
// Dying unit, does nothing further to it.
111
c.Assert(err, jc.ErrorIsNil)
112
c.Assert(u1.Life(), gc.Equals, state.Dying)
115
func (s *deployerSuite) TestRemoveNonAlivePrincipals(c *gc.C) {
116
// Create a service, and a couple of units.
117
svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
118
u0, err := svc.AddUnit()
119
c.Assert(err, jc.ErrorIsNil)
120
u1, err := svc.AddUnit()
121
c.Assert(err, jc.ErrorIsNil)
123
// Assign the units to the machine, and set them to Dying/Dead.
124
err = u0.AssignToMachine(s.machine)
125
c.Assert(err, jc.ErrorIsNil)
126
err = u0.EnsureDead()
127
c.Assert(err, jc.ErrorIsNil)
128
err = u1.AssignToMachine(s.machine)
129
c.Assert(err, jc.ErrorIsNil)
130
// note: this is not a sane state; for the unit to have a status it must
131
// have been deployed. But it's instructive to check that the right thing
132
// would happen if it were possible to have a dying unit in this situation.
133
err = u1.SetAgentStatus(state.StatusIdle, "", nil)
134
c.Assert(err, jc.ErrorIsNil)
136
c.Assert(err, jc.ErrorIsNil)
138
// When the deployer is started, in each case (1) no unit agent is deployed
139
// and (2) the non-Alive unit is been removed from state.
140
dep, ctx := s.makeDeployerAndContext(c)
142
s.waitFor(c, isRemoved(s.State, u0.Name()))
143
s.waitFor(c, isRemoved(s.State, u1.Name()))
144
s.waitFor(c, isDeployed(ctx))
147
func (s *deployerSuite) prepareSubordinates(c *gc.C) (*state.Unit, []*state.RelationUnit) {
148
svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
149
u, err := svc.AddUnit()
150
c.Assert(err, jc.ErrorIsNil)
151
err = u.AssignToMachine(s.machine)
152
c.Assert(err, jc.ErrorIsNil)
153
rus := []*state.RelationUnit{}
154
logging := s.AddTestingCharm(c, "logging")
155
for _, name := range []string{"subsvc0", "subsvc1"} {
156
s.AddTestingService(c, name, logging)
157
eps, err := s.State.InferEndpoints("wordpress", name)
158
c.Assert(err, jc.ErrorIsNil)
159
rel, err := s.State.AddRelation(eps...)
160
c.Assert(err, jc.ErrorIsNil)
161
ru, err := rel.Unit(u)
162
c.Assert(err, jc.ErrorIsNil)
163
rus = append(rus, ru)
168
func (s *deployerSuite) TestDeployRecallRemoveSubordinates(c *gc.C) {
169
// Create a deployer acting on behalf of the principal.
170
u, rus := s.prepareSubordinates(c)
171
dep, ctx := s.makeDeployerAndContext(c)
174
// Add a subordinate, and wait for it to be deployed.
175
err := rus[0].EnterScope(nil)
176
c.Assert(err, jc.ErrorIsNil)
177
sub0, err := s.State.Unit("subsvc0/0")
178
c.Assert(err, jc.ErrorIsNil)
179
// Make sure the principal is deployed first, then the subordinate
180
s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name()))
183
err = rus[1].EnterScope(nil)
184
c.Assert(err, jc.ErrorIsNil)
185
sub1, err := s.State.Unit("subsvc1/0")
186
c.Assert(err, jc.ErrorIsNil)
187
s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name()))
189
// Set one to Dying; check nothing happens.
191
c.Assert(err, jc.ErrorIsNil)
193
c.Assert(isRemoved(s.State, sub1.Name())(c), jc.IsFalse)
194
s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name()))
196
// Set the other to Dead; check it's recalled and removed.
197
err = sub0.EnsureDead()
198
c.Assert(err, jc.ErrorIsNil)
199
s.waitFor(c, isDeployed(ctx, u.Name(), sub1.Name()))
200
s.waitFor(c, isRemoved(s.State, sub0.Name()))
203
func (s *deployerSuite) TestNonAliveSubordinates(c *gc.C) {
204
// Add two subordinate units and set them to Dead/Dying respectively.
205
_, rus := s.prepareSubordinates(c)
206
err := rus[0].EnterScope(nil)
207
c.Assert(err, jc.ErrorIsNil)
208
sub0, err := s.State.Unit("subsvc0/0")
209
c.Assert(err, jc.ErrorIsNil)
210
err = sub0.EnsureDead()
211
c.Assert(err, jc.ErrorIsNil)
212
err = rus[1].EnterScope(nil)
213
c.Assert(err, jc.ErrorIsNil)
214
sub1, err := s.State.Unit("subsvc1/0")
215
c.Assert(err, jc.ErrorIsNil)
217
c.Assert(err, jc.ErrorIsNil)
219
// When we start a new deployer, neither unit will be deployed and
220
// both will be removed.
221
dep, _ := s.makeDeployerAndContext(c)
223
s.waitFor(c, isRemoved(s.State, sub0.Name()))
224
s.waitFor(c, isRemoved(s.State, sub1.Name()))
227
func (s *deployerSuite) waitFor(c *gc.C, t func(c *gc.C) bool) {
228
s.BackingState.StartSync()
232
timeout := time.After(coretesting.LongWait)
237
case <-time.After(coretesting.ShortWait):
245
func isDeployed(ctx deployer.Context, expected ...string) func(*gc.C) bool {
246
return func(c *gc.C) bool {
247
sort.Strings(expected)
248
current, err := ctx.DeployedUnits()
249
c.Assert(err, jc.ErrorIsNil)
250
sort.Strings(current)
251
return strings.Join(expected, ":") == strings.Join(current, ":")
255
func isRemoved(st *state.State, name string) func(*gc.C) bool {
256
return func(c *gc.C) bool {
257
_, err := st.Unit(name)
258
if errors.IsNotFound(err) {
261
c.Assert(err, jc.ErrorIsNil)
266
func stop(c *gc.C, w worker.Worker) {
267
c.Assert(worker.Stop(w), gc.IsNil)
270
type fakeContext struct {
272
agentConfig agent.Config
275
func (ctx *fakeContext) IsUnitInstalled(unitName string) (bool, error) {
279
func (ctx *fakeContext) DeployedUnits() ([]string, error) {
280
return []string{}, nil
283
func (ctx *fakeContext) AgentConfig() agent.Config {
284
return ctx.agentConfig
287
func (s *deployerSuite) TestDeployFailsWhenUnitAlreadyInstalled(c *gc.C) {
288
// Add a new unit and assign to machine
289
svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
290
u, err := svc.AddUnit()
291
c.Assert(err, jc.ErrorIsNil)
292
err = u.AssignToMachine(s.machine)
293
c.Assert(err, jc.ErrorIsNil)
295
// Make deployer with our mocked out context
296
ctx := &fakeContext{agentConfig: agentConfig(s.machine.Tag(), s.dataDir, s.logDir)}
297
dep := deployer.NewDeployer(s.deployerState, ctx)
300
c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" is already installed`)