github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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/runner" 18 "github.com/juju/juju/worker/uniter/runner/jujuc" 19 ) 20 21 type FlushContextSuite struct { 22 HookContextSuite 23 testing.Stub 24 } 25 26 var _ = gc.Suite(&FlushContextSuite{}) 27 28 // StubMetricsReader is a stub implementation of the metrics reader. 29 type StubMetricsReader struct { 30 *testing.Stub 31 Batches []runner.MetricsBatch 32 } 33 34 // Open implements the MetricsReader interface. 35 func (mr *StubMetricsReader) Open() ([]runner.MetricsBatch, error) { 36 mr.MethodCall(mr, "Open") 37 return mr.Batches, mr.NextErr() 38 } 39 40 // Remove implements the MetricsReader interface. 41 func (mr *StubMetricsReader) Remove(uuid string) error { 42 mr.MethodCall(mr, "Remove", uuid) 43 return mr.NextErr() 44 } 45 46 // Close implements the MetricsReader interface. 47 func (mr *StubMetricsReader) Close() error { 48 mr.MethodCall(mr, "Close") 49 return mr.NextErr() 50 } 51 52 func (s *FlushContextSuite) TestRunHookRelationFlushingError(c *gc.C) { 53 uuid, err := utils.NewUUID() 54 c.Assert(err, jc.ErrorIsNil) 55 ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies) 56 57 // Mess with multiple relation settings. 58 relCtx0, ok := ctx.Relation(0) 59 c.Assert(ok, jc.IsTrue) 60 node0, err := relCtx0.Settings() 61 c.Assert(err, jc.ErrorIsNil) 62 node0.Set("foo", "1") 63 relCtx1, ok := ctx.Relation(1) 64 c.Assert(ok, jc.IsTrue) 65 node1, err := relCtx1.Settings() 66 c.Assert(err, jc.ErrorIsNil) 67 node1.Set("bar", "2") 68 69 // Flush the context with a failure. 70 err = ctx.FlushContext("some badge", errors.New("blam pow")) 71 c.Assert(err, gc.ErrorMatches, "blam pow") 72 73 // Check that the changes have not been written to state. 74 settings0, err := s.relunits[0].ReadSettings("u/0") 75 c.Assert(err, jc.ErrorIsNil) 76 c.Assert(settings0, gc.DeepEquals, map[string]interface{}{"relation-name": "db0"}) 77 settings1, err := s.relunits[1].ReadSettings("u/0") 78 c.Assert(err, jc.ErrorIsNil) 79 c.Assert(settings1, gc.DeepEquals, map[string]interface{}{"relation-name": "db1"}) 80 } 81 82 func (s *FlushContextSuite) TestRunHookRelationFlushingSuccess(c *gc.C) { 83 // Create a charm with a working hook, and mess with settings again. 84 uuid, err := utils.NewUUID() 85 c.Assert(err, jc.ErrorIsNil) 86 ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies) 87 88 // Mess with multiple relation settings. 89 relCtx0, ok := ctx.Relation(0) 90 c.Assert(ok, jc.IsTrue) 91 node0, err := relCtx0.Settings() 92 c.Assert(err, jc.ErrorIsNil) 93 node0.Set("baz", "3") 94 relCtx1, ok := ctx.Relation(1) 95 c.Assert(ok, jc.IsTrue) 96 node1, err := relCtx1.Settings() 97 c.Assert(err, jc.ErrorIsNil) 98 node1.Set("qux", "4") 99 100 // Flush the context with a success. 101 err = ctx.FlushContext("some badge", nil) 102 c.Assert(err, jc.ErrorIsNil) 103 104 // Check that the changes have been written to state. 105 settings0, err := s.relunits[0].ReadSettings("u/0") 106 c.Assert(err, jc.ErrorIsNil) 107 c.Assert(settings0, gc.DeepEquals, map[string]interface{}{ 108 "relation-name": "db0", 109 "baz": "3", 110 }) 111 settings1, err := s.relunits[1].ReadSettings("u/0") 112 c.Assert(err, jc.ErrorIsNil) 113 c.Assert(settings1, gc.DeepEquals, map[string]interface{}{ 114 "relation-name": "db1", 115 "qux": "4", 116 }) 117 } 118 119 func (s *FlushContextSuite) TestRunHookMetricSendingSuccess(c *gc.C) { 120 uuid, err := utils.NewUUID() 121 c.Assert(err, jc.ErrorIsNil) 122 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings")) 123 124 now := time.Now() 125 err = ctx.AddMetric("pings", "50", now) 126 c.Assert(err, jc.ErrorIsNil) 127 128 // Flush the context with a success. 129 err = ctx.FlushContext("some badge", nil) 130 c.Assert(err, jc.ErrorIsNil) 131 132 metricBatches, err := s.State.MetricBatches() 133 c.Assert(err, jc.ErrorIsNil) 134 c.Assert(metricBatches, gc.HasLen, 1) 135 metrics := metricBatches[0].Metrics() 136 c.Assert(metrics, gc.HasLen, 1) 137 c.Assert(metrics[0].Key, gc.Equals, "pings") 138 c.Assert(metrics[0].Value, gc.Equals, "50") 139 } 140 141 func (s *FlushContextSuite) TestRunHookMetricSendingGetDuplicate(c *gc.C) { 142 uuid := utils.MustNewUUID() 143 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings")) 144 145 // Send batches once. 146 batches := []runner.MetricsBatch{ 147 { 148 CharmURL: s.meteredCharm.URL().String(), 149 UUID: utils.MustNewUUID().String(), 150 Created: time.Now(), 151 Metrics: []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}}, 152 }, { 153 CharmURL: s.meteredCharm.URL().String(), 154 UUID: utils.MustNewUUID().String(), 155 Created: time.Now(), 156 Metrics: []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}}, 157 }, 158 } 159 160 reader := &StubMetricsReader{ 161 Stub: &s.Stub, 162 Batches: batches, 163 } 164 165 runner.PatchMetricsReader(ctx, reader) 166 167 // Flush the context with a success. 168 err := ctx.FlushContext("some badge", nil) 169 c.Assert(err, jc.ErrorIsNil) 170 171 // Check stub calls. 172 s.Stub.CheckCallNames(c, "Open", "Remove", "Remove", "Close") 173 s.Stub.ResetCalls() 174 metricBatches, err := s.State.MetricBatches() 175 c.Assert(err, jc.ErrorIsNil) 176 c.Assert(metricBatches, gc.HasLen, 2) 177 178 // Create a new context with a duplicate metrics batch. 179 uuid = utils.MustNewUUID() 180 ctx = s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings")) 181 runner.PatchMetricsReader(ctx, reader) 182 183 newBatches := []runner.MetricsBatch{ 184 batches[0], 185 { 186 CharmURL: s.meteredCharm.URL().String(), 187 UUID: utils.MustNewUUID().String(), 188 Created: time.Now(), 189 Metrics: []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}}, 190 }, 191 } 192 reader.Batches = newBatches 193 194 // Flush the context with a success. 195 err = ctx.FlushContext("some badge", nil) 196 c.Assert(err, jc.ErrorIsNil) 197 198 // Check stub calls. 199 s.Stub.CheckCallNames(c, "Open", "Remove", "Remove", "Close") 200 201 metricBatches, err = s.State.MetricBatches() 202 c.Assert(err, jc.ErrorIsNil) 203 // Only one additional metric has been recorded. 204 c.Assert(metricBatches, gc.HasLen, 3) 205 206 } 207 208 func (s *FlushContextSuite) TestRunHookMetricSendingFailedByServer(c *gc.C) { 209 uuid, err := utils.NewUUID() 210 c.Assert(err, jc.ErrorIsNil) 211 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("pings")) 212 213 // Send batches once. 214 batches := []runner.MetricsBatch{ 215 { 216 CharmURL: s.meteredCharm.URL().String(), 217 UUID: utils.MustNewUUID().String(), 218 Created: time.Now(), 219 Metrics: []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}}, 220 }, { 221 CharmURL: s.meteredCharm.URL().String(), 222 UUID: utils.MustNewUUID().String(), 223 Created: time.Now(), 224 Metrics: []jujuc.Metric{{Key: "pings", Value: "1", Time: time.Now()}}, 225 }, 226 } 227 228 reader := &StubMetricsReader{ 229 Stub: &s.Stub, 230 Batches: batches, 231 } 232 233 restoreRunner := runner.PatchMetricsReader(ctx, reader) 234 defer restoreRunner() 235 236 restoreSender := runner.PatchMetricsSender(ctx, func(batches []params.MetricBatch) (map[string]error, error) { 237 responses := make(map[string]error, len(batches)) 238 for i := range responses { 239 responses[i] = errors.New("failed to store") 240 } 241 return responses, nil 242 }) 243 defer restoreSender() 244 245 // Flush the context. 246 err = ctx.FlushContext("some badge", nil) 247 c.Assert(err, jc.ErrorIsNil) 248 249 // Check stub calls, metrics should not be removed. 250 s.Stub.CheckCallNames(c, "Open", "Close") 251 s.Stub.ResetCalls() 252 } 253 254 func (s *FlushContextSuite) TestRunHookNoMetricSendingOnFailure(c *gc.C) { 255 uuid, err := utils.NewUUID() 256 c.Assert(err, jc.ErrorIsNil) 257 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, s.metricsDefinition("key")) 258 259 now := time.Now() 260 ctx.AddMetric("key", "50", now) 261 262 // Flush the context with an error. 263 err = ctx.FlushContext("some badge", errors.New("boom squelch")) 264 c.Assert(err, gc.ErrorMatches, "boom squelch") 265 266 metricBatches, err := s.State.MetricBatches() 267 c.Assert(err, jc.ErrorIsNil) 268 c.Assert(metricBatches, gc.HasLen, 0) 269 } 270 271 func (s *FlushContextSuite) TestRunHookMetricSendingDisabled(c *gc.C) { 272 uuid, err := utils.NewUUID() 273 c.Assert(err, jc.ErrorIsNil) 274 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, false, s.metricsDefinition("key")) 275 276 now := time.Now() 277 err = ctx.AddMetric("key", "50", now) 278 c.Assert(err, gc.ErrorMatches, "metrics disabled") 279 280 // Flush the context with a success. 281 err = ctx.FlushContext("some badge", nil) 282 c.Assert(err, jc.ErrorIsNil) 283 284 metricBatches, err := s.State.MetricBatches() 285 c.Assert(err, jc.ErrorIsNil) 286 c.Assert(metricBatches, gc.HasLen, 0) 287 } 288 289 func (s *FlushContextSuite) TestRunHookMetricSendingUndeclared(c *gc.C) { 290 uuid, err := utils.NewUUID() 291 c.Assert(err, jc.ErrorIsNil) 292 ctx := s.getMeteredHookContext(c, uuid.String(), -1, "", noProxies, true, nil) 293 294 now := time.Now() 295 err = ctx.AddMetric("key", "50", now) 296 c.Assert(err, gc.ErrorMatches, "metrics disabled") 297 298 // Flush the context with a success. 299 err = ctx.FlushContext("some badge", nil) 300 c.Assert(err, jc.ErrorIsNil) 301 302 metricBatches, err := s.State.MetricBatches() 303 c.Assert(err, jc.ErrorIsNil) 304 c.Assert(metricBatches, gc.HasLen, 0) 305 } 306 307 func (s *FlushContextSuite) TestRunHookOpensAndClosesPendingPorts(c *gc.C) { 308 // Initially, no port ranges are open on the unit or its machine. 309 unitRanges, err := s.unit.OpenedPorts() 310 c.Assert(err, jc.ErrorIsNil) 311 c.Assert(unitRanges, gc.HasLen, 0) 312 machinePorts, err := s.machine.AllPorts() 313 c.Assert(err, jc.ErrorIsNil) 314 c.Assert(machinePorts, gc.HasLen, 0) 315 316 // Add another unit on the same machine. 317 otherUnit, err := s.service.AddUnit() 318 c.Assert(err, jc.ErrorIsNil) 319 err = otherUnit.AssignToMachine(s.machine) 320 c.Assert(err, jc.ErrorIsNil) 321 322 // Open some ports on both units. 323 err = s.unit.OpenPorts("tcp", 100, 200) 324 c.Assert(err, jc.ErrorIsNil) 325 err = otherUnit.OpenPorts("udp", 200, 300) 326 c.Assert(err, jc.ErrorIsNil) 327 328 unitRanges, err = s.unit.OpenedPorts() 329 c.Assert(err, jc.ErrorIsNil) 330 c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{ 331 {100, 200, "tcp"}, 332 }) 333 334 // Get the context. 335 uuid, err := utils.NewUUID() 336 c.Assert(err, jc.ErrorIsNil) 337 ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies) 338 339 // Try opening some ports via the context. 340 err = ctx.OpenPorts("tcp", 100, 200) 341 c.Assert(err, jc.ErrorIsNil) // duplicates are ignored 342 err = ctx.OpenPorts("udp", 200, 300) 343 c.Assert(err, gc.ErrorMatches, `cannot open 200-300/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`) 344 err = ctx.OpenPorts("udp", 100, 200) 345 c.Assert(err, gc.ErrorMatches, `cannot open 100-200/udp \(unit "u/0"\): conflicts with existing 200-300/udp \(unit "u/1"\)`) 346 err = ctx.OpenPorts("udp", 10, 20) 347 c.Assert(err, jc.ErrorIsNil) 348 err = ctx.OpenPorts("tcp", 50, 100) 349 c.Assert(err, gc.ErrorMatches, `cannot open 50-100/tcp \(unit "u/0"\): conflicts with existing 100-200/tcp \(unit "u/0"\)`) 350 err = ctx.OpenPorts("tcp", 50, 80) 351 c.Assert(err, jc.ErrorIsNil) 352 err = ctx.OpenPorts("tcp", 40, 90) 353 c.Assert(err, gc.ErrorMatches, `cannot open 40-90/tcp \(unit "u/0"\): conflicts with 50-80/tcp requested earlier`) 354 355 // Now try closing some ports as well. 356 err = ctx.ClosePorts("udp", 8080, 8088) 357 c.Assert(err, jc.ErrorIsNil) // not existing -> ignored 358 err = ctx.ClosePorts("tcp", 100, 200) 359 c.Assert(err, jc.ErrorIsNil) 360 err = ctx.ClosePorts("tcp", 100, 200) 361 c.Assert(err, jc.ErrorIsNil) // duplicates are ignored 362 err = ctx.ClosePorts("udp", 200, 300) 363 c.Assert(err, gc.ErrorMatches, `cannot close 200-300/udp \(opened by "u/1"\) from "u/0"`) 364 err = ctx.ClosePorts("tcp", 50, 80) 365 c.Assert(err, jc.ErrorIsNil) // still pending -> no longer pending 366 367 // Ensure the ports are not actually changed on the unit yet. 368 unitRanges, err = s.unit.OpenedPorts() 369 c.Assert(err, jc.ErrorIsNil) 370 c.Assert(unitRanges, jc.DeepEquals, []network.PortRange{ 371 {100, 200, "tcp"}, 372 }) 373 374 // Flush the context with a success. 375 err = ctx.FlushContext("some badge", nil) 376 c.Assert(err, jc.ErrorIsNil) 377 378 // Verify the unit ranges are now open. 379 expectUnitRanges := []network.PortRange{ 380 {FromPort: 10, ToPort: 20, Protocol: "udp"}, 381 } 382 unitRanges, err = s.unit.OpenedPorts() 383 c.Assert(err, jc.ErrorIsNil) 384 c.Assert(unitRanges, jc.DeepEquals, expectUnitRanges) 385 } 386 387 func (s *FlushContextSuite) TestRunHookAddStorageOnFailure(c *gc.C) { 388 // Get the context. 389 uuid, err := utils.NewUUID() 390 c.Assert(err, jc.ErrorIsNil) 391 ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies) 392 c.Assert(ctx.UnitName(), gc.Equals, "u/0") 393 394 size := uint64(1) 395 ctx.AddUnitStorage( 396 map[string]params.StorageConstraints{ 397 "allecto": params.StorageConstraints{Size: &size}, 398 }) 399 400 // Flush the context with an error. 401 msg := "test fail run hook" 402 err = ctx.FlushContext("test fail run hook", errors.New(msg)) 403 c.Assert(errors.Cause(err), gc.ErrorMatches, msg) 404 405 all, err := s.State.AllStorageInstances() 406 c.Assert(err, jc.ErrorIsNil) 407 c.Assert(all, gc.HasLen, 0) 408 } 409 410 func (s *FlushContextSuite) TestRunHookAddUnitStorageOnSuccess(c *gc.C) { 411 // Get the context. 412 uuid, err := utils.NewUUID() 413 c.Assert(err, jc.ErrorIsNil) 414 ctx := s.getHookContext(c, uuid.String(), -1, "", noProxies) 415 c.Assert(ctx.UnitName(), gc.Equals, "u/0") 416 417 size := uint64(1) 418 ctx.AddUnitStorage( 419 map[string]params.StorageConstraints{ 420 "allecto": params.StorageConstraints{Size: &size}, 421 }) 422 423 // Flush the context with a success. 424 err = ctx.FlushContext("success", nil) 425 c.Assert(errors.Cause(err), gc.ErrorMatches, `.*storage "allecto" not found.*`) 426 427 all, err := s.State.AllStorageInstances() 428 c.Assert(err, jc.ErrorIsNil) 429 c.Assert(all, gc.HasLen, 0) 430 }