github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/lifecycle/tally/tally_test.go (about) 1 // Copyright (c) 2020-2021, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package tally 6 7 import ( 8 "io" 9 "testing" 10 "time" 11 12 "github.com/choria-io/go-choria/lifecycle" 13 "github.com/prometheus/client_golang/prometheus" 14 dto "github.com/prometheus/client_model/go" 15 16 . "github.com/onsi/ginkgo/v2" 17 . "github.com/onsi/gomega" 18 "github.com/sirupsen/logrus" 19 ) 20 21 func TestChoria(t *testing.T) { 22 RegisterFailHandler(Fail) 23 RunSpecs(t, "Lifecycle/Tally") 24 } 25 26 var _ = Describe("Tally", func() { 27 var ( 28 logger = logrus.NewEntry(logrus.New()) 29 recorder *Recorder 30 ) 31 32 BeforeEach(func() { 33 logger.Logger.SetOutput(io.Discard) 34 registerStats = false 35 recorder = &Recorder{ 36 active: 1, 37 observed: make(map[string]*observations), 38 options: &options{ 39 Component: "ginkgo", 40 StatPrefix: "tally", 41 Log: logger, 42 }, 43 } 44 recorder.createStats() 45 }) 46 47 Describe("maintenance", func() { 48 It("Should not delete current nodes", func() { 49 event, err := lifecycle.New(lifecycle.Alive, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.3"), lifecycle.Identity("ginkgo.example.net")) 50 Expect(err).ToNot(HaveOccurred()) 51 52 recorder.process(event) 53 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 54 recorder.maintenance() 55 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 56 }) 57 58 It("Should delete old nodes", func() { 59 event, err := lifecycle.New(lifecycle.Alive, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.3"), lifecycle.Identity("ginkgo.example.net")) 60 Expect(err).ToNot(HaveOccurred()) 61 62 recorder.process(event) 63 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 64 65 recorder.observed["ginkgo"].hosts["ginkgo.example.net"].ts = recorder.observed["ginkgo"].hosts["ginkgo.example.net"].ts.Add(-1 * (60 * time.Minute)) 66 recorder.maintenance() 67 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 68 69 recorder.observed["ginkgo"].hosts["ginkgo.example.net"].ts = recorder.observed["ginkgo"].hosts["ginkgo.example.net"].ts.Add(-1 * (90 * time.Minute)) 70 recorder.maintenance() 71 Expect(recorder.observed["ginkgo"].hosts).To(BeEmpty()) 72 }) 73 }) 74 75 Describe("elections", func() { 76 It("Should correctly label metrics", func() { 77 event, err := lifecycle.New(lifecycle.Startup, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.3"), lifecycle.Identity("ginkgo.example.net")) 78 Expect(err).ToNot(HaveOccurred()) 79 80 recorder.process(event) 81 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 82 83 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 84 85 recorder.lostCb() 86 87 event, err = lifecycle.New(lifecycle.Startup, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.4"), lifecycle.Identity("other.example.net")) 88 Expect(err).ToNot(HaveOccurred()) 89 90 recorder.process(event) 91 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(2)) 92 93 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 94 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.4", "0")).To(Equal(1.0)) 95 96 recorder.wonCb() 97 event, err = lifecycle.New(lifecycle.Startup, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.4"), lifecycle.Identity("foo.example.net")) 98 Expect(err).ToNot(HaveOccurred()) 99 100 recorder.process(event) 101 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(3)) 102 103 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 104 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.4", "0")).To(Equal(1.0)) 105 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.4", "1")).To(Equal(1.0)) 106 }) 107 }) 108 109 Describe("process", func() { 110 Describe("Shutdown Events", func() { 111 It("Should handle existing nodes", func() { 112 event, err := lifecycle.New(lifecycle.Startup, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.3"), lifecycle.Identity("ginkgo.example.net")) 113 Expect(err).ToNot(HaveOccurred()) 114 115 recorder.process(event) 116 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 117 118 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 119 120 event, err = lifecycle.New(lifecycle.Shutdown, lifecycle.Component("ginkgo"), lifecycle.Identity("ginkgo.example.net")) 121 Expect(err).ToNot(HaveOccurred()) 122 recorder.process(event) 123 124 Expect(recorder.observed["ginkgo"].hosts).To(BeEmpty()) 125 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3")).To(Equal(0.0)) 126 }) 127 128 It("Should handle new nodes", func() { 129 event, err := lifecycle.New(lifecycle.Shutdown, lifecycle.Component("ginkgo"), lifecycle.Identity("ginkgo.example.net")) 130 Expect(err).ToNot(HaveOccurred()) 131 recorder.process(event) 132 133 Expect(recorder.observed).To(BeEmpty()) 134 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3")).To(Equal(0.0)) 135 }) 136 }) 137 138 Describe("Startup Events", func() { 139 It("Should handle new nodes", func() { 140 event, err := lifecycle.New(lifecycle.Startup, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.3"), lifecycle.Identity("ginkgo.example.net")) 141 Expect(err).ToNot(HaveOccurred()) 142 143 Expect(recorder.observed).To(BeEmpty()) 144 recorder.process(event) 145 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 146 147 Expect(recorder.observed["ginkgo"].hosts["ginkgo.example.net"].version).To(Equal("1.2.3")) 148 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 149 }) 150 151 It("Should handle existing nodes", func() { 152 event, err := lifecycle.New(lifecycle.Startup, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.3"), lifecycle.Identity("ginkgo.example.net")) 153 Expect(err).ToNot(HaveOccurred()) 154 155 Expect(recorder.observed).To(BeEmpty()) 156 recorder.process(event) 157 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 158 159 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 160 161 event, err = lifecycle.New(lifecycle.Startup, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.4"), lifecycle.Identity("ginkgo.example.net")) 162 Expect(err).ToNot(HaveOccurred()) 163 164 recorder.process(event) 165 166 Expect(recorder.observed["ginkgo"].hosts["ginkgo.example.net"].version).To(Equal("1.2.4")) 167 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(0.0)) 168 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.4", "1")).To(Equal(1.0)) 169 }) 170 }) 171 172 Describe("Governor Events", func() { 173 It("Should handle governor events", func() { 174 event, err := lifecycle.New(lifecycle.Governor, lifecycle.Component("ginkgo"), lifecycle.GovernorName("GINKGO"), lifecycle.GovernorType(lifecycle.GovernorEnterEvent)) 175 Expect(err).ToNot(HaveOccurred()) 176 recorder.process(event) 177 Expect(getPromCountValue(recorder.governorEvents, "ginkgo", "GINKGO", "enter", "1")).To(Equal(1.0)) 178 Expect(getPromCountValue(recorder.governorEvents, "ginkgo", "GINKGO", "exit", "1")).To(Equal(0.0)) 179 180 event, err = lifecycle.New(lifecycle.Governor, lifecycle.Component("ginkgo"), lifecycle.GovernorName("GINKGO"), lifecycle.GovernorType(lifecycle.GovernorExitEvent)) 181 Expect(err).ToNot(HaveOccurred()) 182 recorder.process(event) 183 Expect(getPromCountValue(recorder.governorEvents, "ginkgo", "GINKGO", "enter", "1")).To(Equal(1.0)) 184 Expect(getPromCountValue(recorder.governorEvents, "ginkgo", "GINKGO", "exit", "1")).To(Equal(1.0)) 185 }) 186 }) 187 188 Describe("Alive Events", func() { 189 It("Should handle new hosts", func() { 190 event, err := lifecycle.New(lifecycle.Alive, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.3"), lifecycle.Identity("ginkgo.example.net")) 191 Expect(err).ToNot(HaveOccurred()) 192 193 Expect(recorder.observed).To(BeEmpty()) 194 recorder.process(event) 195 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 196 197 Expect(recorder.observed["ginkgo"].hosts["ginkgo.example.net"].version).To(Equal("1.2.3")) 198 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 199 }) 200 201 It("Should handle old hosts", func() { 202 event, err := lifecycle.New(lifecycle.Alive, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.3"), lifecycle.Identity("ginkgo.example.net")) 203 Expect(err).ToNot(HaveOccurred()) 204 205 recorder.process(event) 206 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 207 Expect(recorder.observed["ginkgo"].hosts["ginkgo.example.net"].version).To(Equal("1.2.3")) 208 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 209 210 recorder.observed["ginkgo"].hosts["ginkgo.example.net"].ts = time.Now().Add(-120 * time.Minute) 211 212 recorder.process(event) 213 214 Expect(recorder.observed["ginkgo"].hosts).To(HaveLen(1)) 215 Expect(recorder.observed["ginkgo"].hosts["ginkgo.example.net"].version).To(Equal("1.2.3")) 216 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 217 Expect(recorder.observed["ginkgo"].hosts["ginkgo.example.net"].ts).To(BeTemporally("~", time.Now(), time.Second)) 218 }) 219 220 It("Should handle updated hosts", func() { 221 event, err := lifecycle.New(lifecycle.Alive, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.3"), lifecycle.Identity("ginkgo.example.net")) 222 Expect(err).ToNot(HaveOccurred()) 223 224 recorder.process(event) 225 Expect(recorder.observed["ginkgo"].hosts["ginkgo.example.net"].version).To(Equal("1.2.3")) 226 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.3", "1")).To(Equal(1.0)) 227 228 event, err = lifecycle.New(lifecycle.Alive, lifecycle.Component("ginkgo"), lifecycle.Version("1.2.4"), lifecycle.Identity("ginkgo.example.net")) 229 Expect(err).ToNot(HaveOccurred()) 230 231 recorder.process(event) 232 Expect(recorder.observed["ginkgo"].hosts["ginkgo.example.net"].version).To(Equal("1.2.4")) 233 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.2", "1")).To(Equal(0.0)) 234 Expect(getPromGaugeValue(recorder.versionsTally, "ginkgo", "1.2.4", "1")).To(Equal(1.0)) 235 }) 236 }) 237 }) 238 }) 239 240 func getPromCountValue(ctr *prometheus.CounterVec, labels ...string) float64 { 241 pb := &dto.Metric{} 242 m, err := ctr.GetMetricWithLabelValues(labels...) 243 if err != nil { 244 return 0 245 } 246 247 if m.Write(pb) != nil { 248 return 0 249 } 250 251 return pb.GetCounter().GetValue() 252 } 253 254 func getPromGaugeValue(ctr *prometheus.GaugeVec, labels ...string) float64 { 255 pb := &dto.Metric{} 256 m, err := ctr.GetMetricWithLabelValues(labels...) 257 if err != nil { 258 return 0 259 } 260 261 if m.Write(pb) != nil { 262 return 0 263 } 264 265 return pb.GetGauge().GetValue() 266 }