1
// Copyright 2012-2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
8
"github.com/juju/testing"
9
jc "github.com/juju/testing/checkers"
10
"github.com/juju/utils"
11
gc "gopkg.in/check.v1"
13
"github.com/juju/juju/apiserver/params"
14
"github.com/juju/juju/network"
15
"github.com/juju/juju/worker/metrics/spool"
16
"github.com/juju/juju/worker/uniter/runner/context"
17
runnertesting "github.com/juju/juju/worker/uniter/runner/testing"
20
type FlushContextSuite struct {
25
var _ = gc.Suite(&FlushContextSuite{})
27
func (s *FlushContextSuite) SetUpTest(c *gc.C) {
28
s.HookContextSuite.SetUpTest(c)
32
func (s *FlushContextSuite) TestRunHookRelationFlushingError(c *gc.C) {
35
// Mess with multiple relation settings.
36
relCtx0, err := ctx.Relation(0)
37
c.Assert(err, jc.ErrorIsNil)
38
node0, err := relCtx0.Settings()
39
c.Assert(err, jc.ErrorIsNil)
41
relCtx1, err := ctx.Relation(1)
42
c.Assert(err, jc.ErrorIsNil)
43
node1, err := relCtx1.Settings()
44
c.Assert(err, jc.ErrorIsNil)
47
// Flush the context with a failure.
48
err = ctx.Flush("some badge", errors.New("blam pow"))
49
c.Assert(err, gc.ErrorMatches, "blam pow")
51
// Check that the changes have not been written to state.
52
settings0, err := s.relunits[0].ReadSettings("u/0")
53
c.Assert(err, jc.ErrorIsNil)
54
c.Assert(settings0, gc.DeepEquals, map[string]interface{}{"relation-name": "db0"})
55
settings1, err := s.relunits[1].ReadSettings("u/0")
56
c.Assert(err, jc.ErrorIsNil)
57
c.Assert(settings1, gc.DeepEquals, map[string]interface{}{"relation-name": "db1"})
60
func (s *FlushContextSuite) TestRunHookRelationFlushingSuccess(c *gc.C) {
63
// Mess with multiple relation settings.
64
relCtx0, err := ctx.Relation(0)
65
c.Assert(err, jc.ErrorIsNil)
66
node0, err := relCtx0.Settings()
67
c.Assert(err, jc.ErrorIsNil)
69
relCtx1, err := ctx.Relation(1)
70
c.Assert(err, jc.ErrorIsNil)
71
node1, err := relCtx1.Settings()
72
c.Assert(err, jc.ErrorIsNil)
75
// Flush the context with a success.
76
err = ctx.Flush("some badge", nil)
77
c.Assert(err, jc.ErrorIsNil)
79
// Check that the changes have been written to state.
80
settings0, err := s.relunits[0].ReadSettings("u/0")
81
c.Assert(err, jc.ErrorIsNil)
82
c.Assert(settings0, gc.DeepEquals, map[string]interface{}{
83
"relation-name": "db0",
86
settings1, err := s.relunits[1].ReadSettings("u/0")
87
c.Assert(err, jc.ErrorIsNil)
88
c.Assert(settings1, gc.DeepEquals, map[string]interface{}{
89
"relation-name": "db1",
94
func (s *FlushContextSuite) TestRunHookOpensAndClosesPendingPorts(c *gc.C) {
95
// Initially, no port ranges are open on the unit or its machine.
96
unitRanges, err := s.unit.OpenedPorts()
97
c.Assert(err, jc.ErrorIsNil)
98
c.Assert(unitRanges, gc.HasLen, 0)
99
machinePorts, err := s.machine.AllPorts()
100
c.Assert(err, jc.ErrorIsNil)
101
c.Assert(machinePorts, gc.HasLen, 0)
103
// Add another unit on the same machine.
104
otherUnit, err := s.service.AddUnit()
105
c.Assert(err, jc.ErrorIsNil)
106
err = otherUnit.AssignToMachine(s.machine)
107
c.Assert(err, jc.ErrorIsNil)
109
// Open some ports on both units.
110
err = s.unit.OpenPorts("tcp", 100, 200)
111
c.Assert(err, jc.ErrorIsNil)
112
err = otherUnit.OpenPorts("udp", 200, 300)
113
c.Assert(err, jc.ErrorIsNil)
115
unitRanges, err = s.unit.OpenedPorts()
116
c.Assert(err, jc.ErrorIsNil)
117
c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{
123
// Try opening some ports via the context.
124
err = ctx.OpenPorts("tcp", 100, 200)
125
c.Assert(err, jc.ErrorIsNil) // duplicates are ignored
126
err = ctx.OpenPorts("udp", 200, 300)
127
c.Assert(err, gc.ErrorMatches, `cannot open 200-300/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`)
128
err = ctx.OpenPorts("udp", 100, 200)
129
c.Assert(err, gc.ErrorMatches, `cannot open 100-200/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`)
130
err = ctx.OpenPorts("udp", 10, 20)
131
c.Assert(err, jc.ErrorIsNil)
132
err = ctx.OpenPorts("tcp", 50, 100)
133
c.Assert(err, gc.ErrorMatches, `cannot open 50-100/tcp \(unit "u/0"\): conflicts with existing 100-200/tcp \(unit "u/0"\)`)
134
err = ctx.OpenPorts("tcp", 50, 80)
135
c.Assert(err, jc.ErrorIsNil)
136
err = ctx.OpenPorts("tcp", 40, 90)
137
c.Assert(err, gc.ErrorMatches, `cannot open 40-90/tcp \(unit "u/0"\): conflicts with 50-80/tcp requested earlier`)
139
// Now try closing some ports as well.
140
err = ctx.ClosePorts("udp", 8080, 8088)
141
c.Assert(err, jc.ErrorIsNil) // not existing -> ignored
142
err = ctx.ClosePorts("tcp", 100, 200)
143
c.Assert(err, jc.ErrorIsNil)
144
err = ctx.ClosePorts("tcp", 100, 200)
145
c.Assert(err, jc.ErrorIsNil) // duplicates are ignored
146
err = ctx.ClosePorts("udp", 200, 300)
147
c.Assert(err, gc.ErrorMatches, `cannot close 200-300/udp \(opened by "u/1"\) from "u/0"`)
148
err = ctx.ClosePorts("tcp", 50, 80)
149
c.Assert(err, jc.ErrorIsNil) // still pending -> no longer pending
151
// Ensure the ports are not actually changed on the unit yet.
152
unitRanges, err = s.unit.OpenedPorts()
153
c.Assert(err, jc.ErrorIsNil)
154
c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{
158
// Flush the context with a success.
159
err = ctx.Flush("some badge", nil)
160
c.Assert(err, jc.ErrorIsNil)
162
// Verify the unit ranges are now open.
163
expectUnitRanges := []network.PortRange{
164
{FromPort: 10, ToPort: 20, Protocol: "udp"},
166
unitRanges, err = s.unit.OpenedPorts()
167
c.Assert(err, jc.ErrorIsNil)
168
c.Assert(unitRanges, jc.DeepEquals, expectUnitRanges)
171
func (s *FlushContextSuite) TestRunHookAddStorageOnFailure(c *gc.C) {
173
c.Assert(ctx.UnitName(), gc.Equals, "u/0")
177
map[string]params.StorageConstraints{
178
"allecto": params.StorageConstraints{Size: &size},
181
// Flush the context with an error.
182
msg := "test fail run hook"
183
err := ctx.Flush("test fail run hook", errors.New(msg))
184
c.Assert(errors.Cause(err), gc.ErrorMatches, msg)
186
all, err := s.State.AllStorageInstances()
187
c.Assert(err, jc.ErrorIsNil)
188
c.Assert(all, gc.HasLen, 0)
191
func (s *FlushContextSuite) TestRunHookAddUnitStorageOnSuccess(c *gc.C) {
193
c.Assert(ctx.UnitName(), gc.Equals, "u/0")
197
map[string]params.StorageConstraints{
198
"allecto": params.StorageConstraints{Size: &size},
201
// Flush the context with a success.
202
err := ctx.Flush("success", nil)
203
c.Assert(errors.Cause(err), gc.ErrorMatches, `.*storage "allecto" not found.*`)
205
all, err := s.State.AllStorageInstances()
206
c.Assert(err, jc.ErrorIsNil)
207
c.Assert(all, gc.HasLen, 0)
210
func (s *HookContextSuite) context(c *gc.C) *context.HookContext {
211
uuid, err := utils.NewUUID()
212
c.Assert(err, jc.ErrorIsNil)
213
return s.getHookContext(c, uuid.String(), -1, "", noProxies)
216
func (s *FlushContextSuite) TestBuiltinMetricNotGeneratedIfNotDefined(c *gc.C) {
217
uuid := utils.MustNewUUID()
218
paths := runnertesting.NewRealPaths(c)
219
ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings"), paths)
220
reader, err := spool.NewJSONMetricReader(
221
paths.GetMetricsSpoolDir(),
224
err = ctx.Flush("some badge", nil)
225
c.Assert(err, jc.ErrorIsNil)
226
batches, err := reader.Read()
227
c.Assert(err, jc.ErrorIsNil)
228
c.Assert(batches, gc.HasLen, 0)