Beispiel #1
0
				migrations = []migration.Migration{fakeMigration102, fakeMigration}
			})

			Describe("reporting", func() {
				var sender *fake.FakeMetricSender

				BeforeEach(func() {
					sender = fake.NewFakeMetricSender()
					metrics.Initialize(sender, nil)
				})

				It("reports the duration that it took to migrate", func() {
					Eventually(migrationProcess.Ready()).Should(BeClosed())
					Expect(migrationsDone).To(BeClosed())

					reportedDuration := sender.GetValue("MigrationDuration")
					Expect(reportedDuration.Value).NotTo(BeZero())
					Expect(reportedDuration.Unit).To(Equal("nanos"))
				})
			})

			It("it sorts the migrations and runs them sequentially", func() {
				Eventually(migrationProcess.Ready()).Should(BeClosed())
				Expect(migrationsDone).To(BeClosed())
				Consistently(fakeETCDDB.SetVersionCallCount).Should(Equal(2))

				_, version := fakeETCDDB.SetVersionArgsForCall(0)
				Expect(version).To(Equal(&models.Version{CurrentVersion: 99, TargetVersion: 102}))

				_, version = fakeETCDDB.SetVersionArgsForCall(1)
				Expect(version).To(Equal(&models.Version{CurrentVersion: 102, TargetVersion: 102}))
			Expect(recorder.Code).To(Equal(http.StatusOK))
		})

		Context("metrices", func() {
			var fakeMetricSender *fake.FakeMetricSender

			BeforeEach(func() {
				fakeMetricSender = fake.NewFakeMetricSender()
				metricBatcher := metricbatcher.New(fakeMetricSender, time.Millisecond)
				metrics.Initialize(fakeMetricSender, metricBatcher)
			})

			requestAndAssert := func(req *http.Request, metricName string) {
				requestStart := time.Now()
				proxy.ServeHTTP(recorder, req)
				metric := fakeMetricSender.GetValue(metricName)
				elapsed := float64(time.Since(requestStart)) / float64(time.Millisecond)

				Expect(metric.Unit).To(Equal("ms"))
				Expect(metric.Value).To(BeNumerically("<", elapsed))

			}
			It("Should emit value metric for recentlogs request", func() {
				close(channelGroupConnector.messages)
				req, _ := http.NewRequest("GET", "/apps/appID123/recentlogs", nil)
				requestAndAssert(req, "dopplerProxy.recentlogsLatency")
			})

			It("Should emit value metric for containermetrics request", func() {
				close(channelGroupConnector.messages)
				req, _ := http.NewRequest("GET", "/apps/appID123/containermetrics", nil)
              "recvAppendRequestCnt": 1234,
              "recvPkgRate": 2.0,
              "recvBandwidthRate": 0.8,

              "sendAppendRequestCnt": 4321
            }
	        `))

					etcd3.RouteToHandler("GET", "/v2/stats/leader", func(w http.ResponseWriter, r *http.Request) {
						http.Redirect(w, r, etcd2.URL(), 302)
					})
				})

				It("should emit them", func() {
					Eventually(func() fake.Metric {
						return sender.GetValue("ETCDLeader")
					}).Should(Equal(fake.Metric{
						Value: 1,
						Unit:  "Metric",
					}))

					Eventually(func() fake.Metric {
						return sender.GetValue("ETCDReceivedBandwidthRate")
					}).Should(Equal(fake.Metric{
						Value: 2,
						Unit:  "B/s",
					}))

					Eventually(func() fake.Metric {
						return sender.GetValue("ETCDSentBandwidthRate")
					}).Should(Equal(fake.Metric{
					sender *fake.FakeMetricSender
				)

				BeforeEach(func() {
					sender = fake.NewFakeMetricSender()
					metrics.Initialize(sender, nil)
				})

				Context("when a connection is received", func() {
					It("emit a metric for the total number of connections", func() {
						_, err := ssh.Dial("tcp", proxyAddress, clientConfig)
						Expect(err).NotTo(HaveOccurred())

						Eventually(
							func() float64 {
								return sender.GetValue("ssh-connections").Value
							},
						).Should(Equal(float64(1)))

						_, err = ssh.Dial("tcp", proxyAddress, clientConfig)
						Expect(err).NotTo(HaveOccurred())

						Eventually(
							func() float64 {
								return sender.GetValue("ssh-connections").Value
							},
						).Should(Equal(float64(2)))
					})
				})

				Context("when a connection is closed", func() {
			metricsReporter.CaptureRoutingResponse(endpoint, &response2xx, time.Now(), time.Millisecond)
			Eventually(func() uint64 { return sender.GetCounter("responses") }).Should(BeEquivalentTo(1))

			metricsReporter.CaptureRoutingResponse(endpoint, &response4xx, time.Now(), time.Millisecond)
			Eventually(func() uint64 { return sender.GetCounter("responses") }).Should(BeEquivalentTo(2))

		})

		It("sends the latency", func() {
			response := http.Response{
				StatusCode: 401,
			}

			metricsReporter.CaptureRoutingResponse(endpoint, &response, time.Now(), 2*time.Second)
			Eventually(func() fake.Metric { return sender.GetValue("latency") }).Should(Equal(
				fake.Metric{
					Value: 2000,
					Unit:  "ms",
				}))
		})

		It("sends the latency for the given component", func() {
			response := http.Response{
				StatusCode: 200,
			}

			endpoint.Tags["component"] = "CloudController"
			metricsReporter.CaptureRoutingResponse(endpoint, &response, time.Now(), 2*time.Second)
			Eventually(func() fake.Metric { return sender.GetValue("latency.CloudController") }).Should(Equal(
				fake.Metric{
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Metrics", func() {
	var fakeMetricSender *fake.FakeMetricSender

	BeforeEach(func() {
		fakeMetricSender = fake.NewFakeMetricSender()
		metrics.Initialize(fakeMetricSender)
	})

	It("delegates SendValue", func() {
		metrics.SendValue("metric", 42.42, "answers")

		Expect(fakeMetricSender.GetValue("metric").Value).To(Equal(42.42))
		Expect(fakeMetricSender.GetValue("metric").Unit).To(Equal("answers"))
	})

	It("delegates IncrementCounter", func() {
		metrics.IncrementCounter("count")

		Expect(fakeMetricSender.GetCounter("count")).To(BeEquivalentTo(1))

		metrics.IncrementCounter("count")

		Expect(fakeMetricSender.GetCounter("count")).To(BeEquivalentTo(2))
	})

	It("delegates AddToCounter", func() {
		metrics.AddToCounter("count", 5)
Beispiel #7
0
			})

			It("responds with 202", func() {
				Expect(responseRecorder.Code).To(Equal(http.StatusAccepted))
			})

			It("logs with the correct session nesting", func() {
				Expect(logger.TestSink.LogMessages()).To(Equal([]string{
					"test.request.serving",
					"test.request.task-auction-handler.create.submitted",
					"test.request.done",
				}))
			})

			It("sends the correct metrics", func() {
				Expect(sender.GetValue("RequestLatency").Value).To(BeNumerically(">", 0))
				Expect(sender.GetCounter("RequestCount")).To(BeEquivalentTo(1))
			})
		})
	})

	Describe("LRP Handler", func() {
		Context("with a valid LRPStart", func() {
			BeforeEach(func() {
				starts := []auctioneer.LRPStartRequest{{
					Indices: []int{2},

					Domain:      "tests",
					ProcessGuid: "some-guid",

					Resource: rep.Resource{
Beispiel #8
0
	Context("When consul is running", func() {
		Context("an error occurs while acquiring the lock", func() {
			BeforeEach(func() {
				lockKey = ""
			})

			It("continues to retry", func() {
				lockProcess = ifrit.Background(lockRunner)
				shouldEventuallyHaveNumSessions(1)
				Consistently(lockProcess.Ready()).ShouldNot(BeClosed())
				Consistently(lockProcess.Wait()).ShouldNot(Receive())

				clock.Increment(retryInterval)
				Eventually(logger).Should(Say("acquire-lock-failed"))
				Eventually(logger).Should(Say("retrying-acquiring-lock"))
				Expect(sender.GetValue(lockHeldMetricName).Value).To(Equal(float64(0)))
			})
		})

		Context("and the lock is available", func() {
			It("acquires the lock", func() {
				lockProcess = ifrit.Background(lockRunner)
				Eventually(lockProcess.Ready()).Should(BeClosed())
				Expect(sender.GetValue(lockUptimeMetricName).Value).Should(Equal(float64(0)))
				Expect(getLockValue()).To(Equal(lockValue))
				Expect(sender.GetValue(lockHeldMetricName).Value).To(Equal(float64(1)))
			})

			Context("and we have acquired the lock", func() {
				JustBeforeEach(func() {
					lockProcess = ifrit.Background(lockRunner)
			})

			It("posts the response builder's result to CC", func() {
				Expect(fakeCCClient.StagingCompleteCallCount()).To(Equal(1))
				guid, payload, _ := fakeCCClient.StagingCompleteArgsForCall(0)
				Expect(guid).To(Equal("the-task-guid"))
				Expect(payload).To(Equal(backendResponseJson))
			})

			Context("when the CC request succeeds", func() {
				It("increments the staging success counter", func() {
					Expect(metricSender.GetCounter("StagingRequestsSucceeded")).To(BeEquivalentTo(1))
				})

				It("emits the time it took to stage succesfully", func() {
					Expect(metricSender.GetValue("StagingRequestSucceededDuration")).To(Equal(fake.Metric{
						Value: float64(stagingDurationNano),
						Unit:  "nanos",
					}))

				})

				It("returns a 200", func() {
					Expect(responseRecorder.Code).To(Equal(200))
				})
			})

			Context("when the CC request fails", func() {
				BeforeEach(func() {
					fakeCCClient.StagingCompleteReturns(&cc_client.BadResponseError{504})
				})
Beispiel #10
0
					defer newTaskConnection.Close()
				case <-time.After(1 * time.Second):
					Fail("Should have been able to open the socket listener")
				}

				newTaskConnection.Write([]byte(SOCKET_PREFIX + expectedMessage + "\n"))

				Eventually(fakeLogSender.GetLogs, 3).Should(HaveLen(1))
				logs := fakeLogSender.GetLogs()
				Expect(logs[0].AppId).To(Equal("5678"))
			})

			Context("metrics", func() {
				It("updates totalApps", func() {
					Eventually(func() float64 {
						return fakeMetricSender.GetValue("totalApps").Value
					}).Should(BeEquivalentTo(3))

					deleteFile()

					Eventually(func() float64 {
						return fakeMetricSender.GetValue("totalApps").Value
					}, 3).Should(BeEquivalentTo(0))
				})
			})
		})

	})

	It("creates the correct structure on forwarded messages and does not contain drain URLs", func() {
		agent.Start()
			reportInterval,
			fakeClock,
		)
		pmn.Start()
	})

	AfterEach(func() {
		pmn.Stop()
	})

	Context("when the report interval elapses", func() {
		It("emits metrics", func() {
			fakeClock.Increment(reportInterval)

			Eventually(func() fake.Metric {
				return sender.GetValue("LoopDevices")
			}).Should(Equal(fake.Metric{
				Value: 33,
				Unit:  "Metric",
			}))

			Eventually(func() fake.Metric {
				return sender.GetValue("BackingStores")
			}).Should(Equal(fake.Metric{
				Value: 12,
				Unit:  "Metric",
			}))

			Eventually(func() fake.Metric {
				return sender.GetValue("DepotDirs")
			}).Should(Equal(fake.Metric{
Beispiel #12
0
		It("does not update the domain", func() {
			Consistently(bbsClient.UpsertDomainCallCount).Should(Equal(0))
		})

		It("sends the creates and updates for the apps it got but not the deletes", func() {
			Eventually(bbsClient.DesireLRPCallCount).Should(Equal(1))
			Eventually(bbsClient.UpdateDesiredLRPCallCount).Should(Equal(2))
			Consistently(bbsClient.RemoveDesiredLRPCallCount).Should(Equal(0))
		})
	})

	Context("when fetching fingerprints succeeds", func() {
		It("emits the total time taken to talk to CC and then update desired state", func() {
			Eventually(bbsClient.UpsertDomainCallCount, 5).Should(Equal(1))

			Eventually(func() fake.Metric { return metricSender.GetValue("DesiredLRPSyncDuration") }).Should(Equal(fake.Metric{
				Value: float64(syncDuration),
				Unit:  "nanos",
			}))
		})

		Context("and the differ discovers desired LRPs to delete", func() {
			It("the processor deletes them", func() {
				Eventually(bbsClient.RemoveDesiredLRPCallCount).Should(Equal(1))
				Consistently(bbsClient.RemoveDesiredLRPCallCount).Should(Equal(1))

				Expect(bbsClient.RemoveDesiredLRPArgsForCall(0)).To(Equal("excess-process-guid"))
			})
		})

		Context("and the differ discovers missing apps", func() {
		Context("Emit", func() {
			JustBeforeEach(func() {
				table.MessagesToEmitReturns(dummyMessagesToEmit)
				table.RouteCountReturns(123)
				syncEvents.Emit <- struct{}{}
			})

			It("emits", func() {
				Eventually(emitter.EmitCallCount).Should(Equal(1))
				Expect(emitter.EmitArgsForCall(0)).To(Equal(dummyMessagesToEmit))
			})

			It("sends a 'routes total' metric", func() {
				Eventually(func() float64 {
					return fakeMetricSender.GetValue("RoutesTotal").Value
				}, 2).Should(BeEquivalentTo(123))
			})

			It("sends a 'synced routes' metric", func() {
				Eventually(func() uint64 {
					return fakeMetricSender.GetCounter("RoutesSynced")
				}, 2).Should(BeEquivalentTo(2))
			})
		})

		Context("Begin & End events", func() {
			currentTag := &models.ModificationTag{Epoch: "abc", Index: 1}
			hostname1 := "foo.example.com"
			hostname2 := "bar.example.com"
			endpoint1 := routing_table.Endpoint{InstanceGuid: "ig-1", Host: "1.1.1.1", Port: 11, ContainerPort: 8080, Evacuating: false, ModificationTag: currentTag}
				case "sentMessageCount":
					Expect(metric.Value).To(Equal(uint64(1)))
				case "sentByteCount":
					Expect(metric.Value).To(Equal(uint64(21)))
				case "receivedMessageCount":
					Expect(metric.Value).To(Equal(uint64(1)))
				case "receivedByteCount":
					Expect(metric.Value).To(Equal(uint64(21)))
				default:
					Fail("Got an invalid metric name: " + metric.Name)
				}
			}
		})

		It("sends metrics using dropsonde", func() {
			Expect(fakeMetricSender.GetValue("currentBufferCount")).To(Equal(fake.Metric{Value: 0, Unit: "Msg"}))
			Expect(fakeMetricSender.GetCounter("sentMessageCount")).To(BeEquivalentTo(1))
			Expect(fakeMetricSender.GetCounter("sentByteCount")).To(BeEquivalentTo(21))
			Expect(fakeMetricSender.GetCounter("receivedMessageCount")).To(BeEquivalentTo(1))
			Expect(fakeMetricSender.GetCounter("receivedByteCount")).To(BeEquivalentTo(21))
		})
	})

	It("doesn't send empty data", func() {
		bufferSize := 4096
		firstMessage := []byte("")
		secondMessage := []byte("hi")

		loggregatorClient.Send(firstMessage)
		loggregatorClient.Send(secondMessage)
				})
				Context("with metrics", func() {
					var sender *fake.FakeMetricSender

					BeforeEach(func() {
						sender = fake.NewFakeMetricSender()
						metrics.Initialize(sender, nil)

					})

					It("should emit mount time on successful mount", func() {
						volumeId := "fake-volume"

						client.Mount(logger, "fakedriver", volumeId, map[string]interface{}{"volume_id": volumeId})

						reportedDuration := sender.GetValue("VolmanMountDuration")
						Expect(reportedDuration.Unit).To(Equal("nanos"))
						Expect(reportedDuration.Value).NotTo(BeZero())
						reportedDuration = sender.GetValue("VolmanMountDurationForfakedriver")
						Expect(reportedDuration.Unit).To(Equal("nanos"))
						Expect(reportedDuration.Value).NotTo(BeZero())
					})

					It("should increment error count on mount failure", func() {
						mountResponse := voldriver.MountResponse{Err: "an error"}
						fakeDriver.MountReturns(mountResponse)

						volumeId := "fake-volume"
						client.Mount(logger, "fakedriver", volumeId, map[string]interface{}{"volume_id": volumeId})
						Expect(sender.GetCounter("VolmanMountErrors")).To(Equal(uint64(1)))
					})
Beispiel #16
0
			handler http.HandlerFunc
		)

		BeforeEach(func() {
			sender = fake.NewFakeMetricSender()
			dropsonde_metrics.Initialize(sender, nil)
			logger := lager.NewLogger("test-session")

			handler = func(w http.ResponseWriter, r *http.Request) { time.Sleep(10) }
			handler = middleware.NewLatencyEmitter(logger).EmitLatency(handler)
		})

		It("reports latency", func() {
			handler.ServeHTTP(nil, nil)

			latency := sender.GetValue("RequestLatency")
			Expect(latency.Value).NotTo(BeZero())
			Expect(latency.Unit).To(Equal("nanos"))
		})
	})

	Describe("RequestCountWrap", func() {
		var (
			sender  *fake.FakeMetricSender
			handler http.HandlerFunc
		)

		BeforeEach(func() {
			sender = fake.NewFakeMetricSender()
			dropsonde_metrics.Initialize(sender, nil)
					{
						LRP:           rep.NewLRP(models.NewActualLRPKey("incompatible-stacks", 0, "domain"), resource),
						AuctionRecord: auctiontypes.AuctionRecord{PlacementError: auctiontypes.ErrorCellMismatch.Error()},
					},
				},
				FailedTasks: []auctiontypes.TaskAuction{
					{
						Task:          rep.NewTask("failed-task", "domain", resource),
						AuctionRecord: auctiontypes.AuctionRecord{PlacementError: rep.ErrorInsufficientResources.Error()},
					},
				},
			})

			Expect(metricSender.GetCounter("AuctioneerLRPAuctionsStarted")).To(BeNumerically("==", 1))
			Expect(metricSender.GetCounter("AuctioneerTaskAuctionsStarted")).To(BeNumerically("==", 1))
			Expect(metricSender.GetCounter("AuctioneerLRPAuctionsFailed")).To(BeNumerically("==", 2))
			Expect(metricSender.GetCounter("AuctioneerTaskAuctionsFailed")).To(BeNumerically("==", 1))
		})
	})

	Describe("FetchStatesCompleted", func() {
		It("should adjust the metric counters", func() {
			delegate.FetchStatesCompleted(1 * time.Second)

			sentMetric := metricSender.GetValue("AuctioneerFetchStatesDuration")
			Expect(sentMetric.Value).To(Equal(1e+09))
			Expect(sentMetric.Unit).To(Equal("nanos"))
		})
	})
})
Beispiel #18
0
	JustBeforeEach(func() {
		reporter = ifrit.Envoke(&metrics.Reporter{
			ExecutorSource: executorClient,
			Interval:       reportInterval,
			Logger:         logger,
		})
	})

	AfterEach(func() {
		reporter.Signal(os.Interrupt)
		Eventually(reporter.Wait()).Should(Receive())
	})

	It("reports the current capacity on the given interval", func() {
		Eventually(func() fake.Metric {
			return sender.GetValue("CapacityTotalMemory")
		}, reportInterval*2).Should(Equal(fake.Metric{
			Value: 1024,
			Unit:  "MiB",
		}))

		Eventually(func() fake.Metric {
			return sender.GetValue("CapacityTotalDisk")
		}, reportInterval*2).Should(Equal(fake.Metric{
			Value: 2048,
			Unit:  "MiB",
		}))

		Eventually(func() fake.Metric {
			return sender.GetValue("CapacityTotalContainers")
		}, reportInterval*2).Should(Equal(fake.Metric{
Beispiel #19
0
			})

			It("pushes them onto the queue", func() {
				Eventually(fakeQueue.PushCallCount).Should(Equal(2))

				enqueuedOperations := make([]operationq.Operation, 0, 2)
				enqueuedOperations = append(enqueuedOperations, fakeQueue.PushArgsForCall(0))
				enqueuedOperations = append(enqueuedOperations, fakeQueue.PushArgsForCall(1))

				Expect(enqueuedOperations).To(ConsistOf(operation1, operation2))
			})

			It("emits the duration it took to generate the batch operations", func() {
				Eventually(fakeQueue.PushCallCount).Should(Equal(2))

				reportedDuration := sender.GetValue("RepBulkSyncDuration")
				Expect(reportedDuration.Unit).To(Equal("nanos"))
				Expect(reportedDuration.Value).To(BeNumerically("==", 10*time.Second))
			})
		})

		Context("when generating the batch operations fails", func() {
			disaster := errors.New("nope")

			BeforeEach(func() {
				fakeGenerator.BatchOperationsReturns(nil, disaster)
			})

			It("logs the error", func() {
				Eventually(logger).Should(gbytes.Say("failed-to-generate-operations"))
				Eventually(logger).Should(gbytes.Say("nope"))
Beispiel #20
0
	})

	JustBeforeEach(func() {
		runner = encryptor.New(logger, fakeDB, keyManager, cryptor, clock.NewClock())
		encryptorProcess = ifrit.Background(runner)
	})

	AfterEach(func() {
		ginkgomon.Kill(encryptorProcess)
	})

	It("reports the duration that it took to encrypt", func() {
		Eventually(encryptorProcess.Ready()).Should(BeClosed())
		Eventually(logger.LogMessages).Should(ContainElement("test.encryptor.encryption-finished"))

		reportedDuration := sender.GetValue("EncryptionDuration")
		Expect(reportedDuration.Value).NotTo(BeZero())
		Expect(reportedDuration.Unit).To(Equal("nanos"))
	})

	Context("when there is no current encryption key", func() {
		BeforeEach(func() {
			fakeDB.EncryptionKeyLabelReturns("", models.ErrResourceNotFound)
		})

		It("encrypts all the existing records", func() {
			Eventually(encryptorProcess.Ready()).Should(BeClosed())
			Eventually(logger.LogMessages).Should(ContainElement("test.encryptor.encryption-finished"))
			Expect(fakeDB.PerformEncryptionCallCount()).To(Equal(1))
		})
			taskGuid  = "some-guid"
			taskGuid2 = "some-other-guid"
			domain    = "some-domain"
			cellId    = "cell-id"
		)

		JustBeforeEach(func() {
			etcdDB.ConvergeTasks(logger, kickTasksDuration, expirePendingTaskDuration, expireCompletedTaskDuration)
		})

		It("bumps the convergence counter", func() {
			Expect(sender.GetCounter("ConvergenceTaskRuns")).To(Equal(uint64(1)))
		})

		It("reports the duration that it took to converge", func() {
			reportedDuration := sender.GetValue("ConvergenceTaskDuration")
			Expect(reportedDuration.Unit).To(Equal("nanos"))
			Expect(reportedDuration.Value).NotTo(BeZero())
		})

		It("emits -1 metrics", func() {
			Expect(sender.GetValue("TasksPending").Value).To(Equal(float64(-1)))
			Expect(sender.GetValue("TasksRunning").Value).To(Equal(float64(-1)))
			Expect(sender.GetValue("TasksCompleted").Value).To(Equal(float64(-1)))
			Expect(sender.GetValue("TasksResolving").Value).To(Equal(float64(-1)))
		})

		Context("when a Task is malformed", func() {
			BeforeEach(func() {
				etcdHelper.CreateMalformedTask(taskGuid)
			})
		_, err = sqlDB.CreateUnclaimedActualLRP(logger, &models.ActualLRPKey{ProcessGuid: processGuid, Index: 0, Domain: domain})
		Expect(err).NotTo(HaveOccurred())
		queryStr = `UPDATE actual_lrps SET evacuating = ?, expire_time = ? WHERE process_guid = ?`
		if test_helpers.UsePostgres() {
			queryStr = test_helpers.ReplaceQuestionMarks(queryStr)
		}
		_, err = db.Exec(queryStr, true, fakeClock.Now().UnixNano(), processGuid)
		Expect(err).NotTo(HaveOccurred())

		fakeClock.Increment(1 * time.Second)
	})

	Describe("general metrics", func() {
		It("emits a metric for domains", func() {
			sqlDB.ConvergeLRPs(logger, cellSet)
			Expect(sender.GetValue("Domain." + freshDomain).Value).To(Equal(float64(1)))
		})

		It("emits metrics for lrps", func() {
			convergenceLogger := lagertest.NewTestLogger("convergence")
			sqlDB.ConvergeLRPs(convergenceLogger, cellSet)
			Expect(sender.GetValue("LRPsDesired").Value).To(Equal(float64(38)))
			Expect(sender.GetValue("LRPsClaimed").Value).To(Equal(float64(7)))
			Expect(sender.GetValue("LRPsUnclaimed").Value).To(Equal(float64(32))) // 16 fresh + 5 expired + 11 evac
			Expect(sender.GetValue("LRPsRunning").Value).To(Equal(float64(1)))
			Expect(sender.GetValue("CrashedActualLRPs").Value).To(Equal(float64(2)))
			Expect(sender.GetValue("CrashingDesiredLRPs").Value).To(Equal(float64(1)))
			Consistently(convergenceLogger).ShouldNot(gbytes.Say("failed-.*"))
		})

		It("emits missing LRP metrics", func() {