github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/uniter/runner/flush_test.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package runner_test 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/network" 17 "github.com/juju/juju/worker/uniter/metrics" 18 "github.com/juju/juju/worker/uniter/runner" 19 ) 20 21 type FlushContextSuite struct { 22 HookContextSuite 23 stub testing.Stub 24 } 25 26 var _ = gc.Suite(&FlushContextSuite{}) 27 28 func (s *FlushContextSuite) SetUpTest(c *gc.C) { 29 s.HookContextSuite.SetUpTest(c) 30 s.stub.ResetCalls() 31 } 32 33 func (s *FlushContextSuite) TestRunHookRelationFlushingError(c *gc.C) { 34 ctx := s.context(c) 35 36 // Mess with multiple relation settings. 37 relCtx0, ok := ctx.Relation(0) 38 c.Assert(ok, jc.IsTrue) 39 node0, err := relCtx0.Settings() 40 c.Assert(err, jc.ErrorIsNil) 41 node0.Set("foo", "1") 42 relCtx1, ok := ctx.Relation(1) 43 c.Assert(ok, jc.IsTrue) 44 node1, err := relCtx1.Settings() 45 c.Assert(err, jc.ErrorIsNil) 46 node1.Set("bar", "2") 47 48 // Flush the context with a failure. 49 err = ctx.Flush("some badge", errors.New("blam pow")) 50 c.Assert(err, gc.ErrorMatches, "blam pow") 51 52 // Check that the changes have not been written to state. 53 settings0, err := s.relunits[0].ReadSettings("u/0") 54 c.Assert(err, jc.ErrorIsNil) 55 c.Assert(settings0, gc.DeepEquals, map[string]interface{}{"relation-name": "db0"}) 56 settings1, err := s.relunits[1].ReadSettings("u/0") 57 c.Assert(err, jc.ErrorIsNil) 58 c.Assert(settings1, gc.DeepEquals, map[string]interface{}{"relation-name": "db1"}) 59 } 60 61 func (s *FlushContextSuite) TestRunHookRelationFlushingSuccess(c *gc.C) { 62 ctx := s.context(c) 63 64 // Mess with multiple relation settings. 65 relCtx0, ok := ctx.Relation(0) 66 c.Assert(ok, jc.IsTrue) 67 node0, err := relCtx0.Settings() 68 c.Assert(err, jc.ErrorIsNil) 69 node0.Set("baz", "3") 70 relCtx1, ok := ctx.Relation(1) 71 c.Assert(ok, jc.IsTrue) 72 node1, err := relCtx1.Settings() 73 c.Assert(err, jc.ErrorIsNil) 74 node1.Set("qux", "4") 75 76 // Flush the context with a success. 77 err = ctx.Flush("some badge", nil) 78 c.Assert(err, jc.ErrorIsNil) 79 80 // Check that the changes have been written to state. 81 settings0, err := s.relunits[0].ReadSettings("u/0") 82 c.Assert(err, jc.ErrorIsNil) 83 c.Assert(settings0, gc.DeepEquals, map[string]interface{}{ 84 "relation-name": "db0", 85 "baz": "3", 86 }) 87 settings1, err := s.relunits[1].ReadSettings("u/0") 88 c.Assert(err, jc.ErrorIsNil) 89 c.Assert(settings1, gc.DeepEquals, map[string]interface{}{ 90 "relation-name": "db1", 91 "qux": "4", 92 }) 93 } 94 95 func (s *FlushContextSuite) TestRunHookOpensAndClosesPendingPorts(c *gc.C) { 96 // Initially, no port ranges are open on the unit or its machine. 97 unitRanges, err := s.unit.OpenedPorts() 98 c.Assert(err, jc.ErrorIsNil) 99 c.Assert(unitRanges, gc.HasLen, 0) 100 machinePorts, err := s.machine.AllPorts() 101 c.Assert(err, jc.ErrorIsNil) 102 c.Assert(machinePorts, gc.HasLen, 0) 103 104 // Add another unit on the same machine. 105 otherUnit, err := s.service.AddUnit() 106 c.Assert(err, jc.ErrorIsNil) 107 err = otherUnit.AssignToMachine(s.machine) 108 c.Assert(err, jc.ErrorIsNil) 109 110 // Open some ports on both units. 111 err = s.unit.OpenPorts("tcp", 100, 200) 112 c.Assert(err, jc.ErrorIsNil) 113 err = otherUnit.OpenPorts("udp", 200, 300) 114 c.Assert(err, jc.ErrorIsNil) 115 116 unitRanges, err = s.unit.OpenedPorts() 117 c.Assert(err, jc.ErrorIsNil) 118 c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{ 119 {100, 200, "tcp"}, 120 }) 121 122 ctx := s.context(c) 123 124 // Try opening some ports via the context. 125 err = ctx.OpenPorts("tcp", 100, 200) 126 c.Assert(err, jc.ErrorIsNil) // duplicates are ignored 127 err = ctx.OpenPorts("udp", 200, 300) 128 c.Assert(err, gc.ErrorMatches, `cannot open 200-300/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`) 129 err = ctx.OpenPorts("udp", 100, 200) 130 c.Assert(err, gc.ErrorMatches, `cannot open 100-200/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`) 131 err = ctx.OpenPorts("udp", 10, 20) 132 c.Assert(err, jc.ErrorIsNil) 133 err = ctx.OpenPorts("tcp", 50, 100) 134 c.Assert(err, gc.ErrorMatches, `cannot open 50-100/tcp \(unit "u/0"\): conflicts with existing 100-200/tcp \(unit "u/0"\)`) 135 err = ctx.OpenPorts("tcp", 50, 80) 136 c.Assert(err, jc.ErrorIsNil) 137 err = ctx.OpenPorts("tcp", 40, 90) 138 c.Assert(err, gc.ErrorMatches, `cannot open 40-90/tcp \(unit "u/0"\): conflicts with 50-80/tcp requested earlier`) 139 140 // Now try closing some ports as well. 141 err = ctx.ClosePorts("udp", 8080, 8088) 142 c.Assert(err, jc.ErrorIsNil) // not existing -> ignored 143 err = ctx.ClosePorts("tcp", 100, 200) 144 c.Assert(err, jc.ErrorIsNil) 145 err = ctx.ClosePorts("tcp", 100, 200) 146 c.Assert(err, jc.ErrorIsNil) // duplicates are ignored 147 err = ctx.ClosePorts("udp", 200, 300) 148 c.Assert(err, gc.ErrorMatches, `cannot close 200-300/udp \(opened by "u/1"\) from "u/0"`) 149 err = ctx.ClosePorts("tcp", 50, 80) 150 c.Assert(err, jc.ErrorIsNil) // still pending -> no longer pending 151 152 // Ensure the ports are not actually changed on the unit yet. 153 unitRanges, err = s.unit.OpenedPorts() 154 c.Assert(err, jc.ErrorIsNil) 155 c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{ 156 {100, 200, "tcp"}, 157 }) 158 159 // Flush the context with a success. 160 err = ctx.Flush("some badge", nil) 161 c.Assert(err, jc.ErrorIsNil) 162 163 // Verify the unit ranges are now open. 164 expectUnitRanges := []network.PortRange{ 165 {FromPort: 10, ToPort: 20, Protocol: "udp"}, 166 } 167 unitRanges, err = s.unit.OpenedPorts() 168 c.Assert(err, jc.ErrorIsNil) 169 c.Assert(unitRanges, jc.DeepEquals, expectUnitRanges) 170 } 171 172 func (s *FlushContextSuite) TestRunHookAddStorageOnFailure(c *gc.C) { 173 ctx := s.context(c) 174 c.Assert(ctx.UnitName(), gc.Equals, "u/0") 175 176 size := uint64(1) 177 ctx.AddUnitStorage( 178 map[string]params.StorageConstraints{ 179 "allecto": params.StorageConstraints{Size: &size}, 180 }) 181 182 // Flush the context with an error. 183 msg := "test fail run hook" 184 err := ctx.Flush("test fail run hook", errors.New(msg)) 185 c.Assert(errors.Cause(err), gc.ErrorMatches, msg) 186 187 all, err := s.State.AllStorageInstances() 188 c.Assert(err, jc.ErrorIsNil) 189 c.Assert(all, gc.HasLen, 0) 190 } 191 192 func (s *FlushContextSuite) TestRunHookAddUnitStorageOnSuccess(c *gc.C) { 193 ctx := s.context(c) 194 c.Assert(ctx.UnitName(), gc.Equals, "u/0") 195 196 size := uint64(1) 197 ctx.AddUnitStorage( 198 map[string]params.StorageConstraints{ 199 "allecto": params.StorageConstraints{Size: &size}, 200 }) 201 202 // Flush the context with a success. 203 err := ctx.Flush("success", nil) 204 c.Assert(errors.Cause(err), gc.ErrorMatches, `.*storage "allecto" not found.*`) 205 206 all, err := s.State.AllStorageInstances() 207 c.Assert(err, jc.ErrorIsNil) 208 c.Assert(all, gc.HasLen, 0) 209 } 210 211 func (s *FlushContextSuite) TestFlushClosesMetricsRecorder(c *gc.C) { 212 uuid := utils.MustNewUUID() 213 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("key"), NewRealPaths(c)) 214 215 runner.PatchMetricsRecorder(ctx, &StubMetricsRecorder{&s.stub}) 216 217 err := ctx.AddMetric("key", "value", time.Now()) 218 219 // Flush the context with a success. 220 err = ctx.Flush("success", nil) 221 c.Assert(err, jc.ErrorIsNil) 222 223 s.stub.CheckCallNames(c, "IsDeclaredMetric", "AddMetric", "Close") 224 } 225 226 func (s *HookContextSuite) context(c *gc.C) *runner.HookContext { 227 uuid, err := utils.NewUUID() 228 c.Assert(err, jc.ErrorIsNil) 229 return s.getHookContext(c, uuid.String(), -1, "", noProxies) 230 } 231 232 func (s *FlushContextSuite) TestBuiltinMetric(c *gc.C) { 233 uuid := utils.MustNewUUID() 234 paths := NewRealPaths(c) 235 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("juju-units"), paths) 236 reader, err := metrics.NewJSONMetricReader( 237 paths.GetMetricsSpoolDir(), 238 ) 239 240 err = ctx.Flush("some badge", nil) 241 c.Assert(err, jc.ErrorIsNil) 242 batches, err := reader.Read() 243 c.Assert(err, jc.ErrorIsNil) 244 c.Assert(batches, gc.HasLen, 1) 245 c.Assert(batches[0].Metrics, gc.HasLen, 1) 246 c.Assert(batches[0].Metrics[0].Key, gc.Equals, "juju-units") 247 c.Assert(batches[0].Metrics[0].Value, gc.Equals, "1") 248 } 249 250 func (s *FlushContextSuite) TestBuiltinMetricNotGeneratedIfNotDefined(c *gc.C) { 251 uuid := utils.MustNewUUID() 252 paths := NewRealPaths(c) 253 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings"), paths) 254 reader, err := metrics.NewJSONMetricReader( 255 paths.GetMetricsSpoolDir(), 256 ) 257 258 err = ctx.Flush("some badge", nil) 259 c.Assert(err, jc.ErrorIsNil) 260 batches, err := reader.Read() 261 c.Assert(err, jc.ErrorIsNil) 262 c.Assert(batches, gc.HasLen, 0) 263 } 264 265 func (s *FlushContextSuite) TestRecorderIsClosedAfterBuiltIn(c *gc.C) { 266 uuid := utils.MustNewUUID() 267 paths := NewRealPaths(c) 268 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("juju-units"), paths) 269 runner.PatchMetricsRecorder(ctx, &StubMetricsRecorder{&s.stub}) 270 271 err := ctx.Flush("some badge", nil) 272 c.Assert(err, jc.ErrorIsNil) 273 s.stub.CheckCallNames(c, "IsDeclaredMetric", "AddMetric", "Close") 274 }