github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/agent/mcorpc/client/options_test.go (about) 1 // Copyright (c) 2020-2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package client 6 7 import ( 8 "fmt" 9 "time" 10 11 "github.com/choria-io/go-choria/inter" 12 imock "github.com/choria-io/go-choria/inter/imocks" 13 "github.com/choria-io/go-choria/message" 14 "github.com/golang/mock/gomock" 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 18 "github.com/choria-io/go-choria/protocol" 19 "github.com/choria-io/go-choria/providers/agent/mcorpc/ddl/agent" 20 ) 21 22 var _ = Describe("McoRPC/Client/Options", func() { 23 var ( 24 mockctl *gomock.Controller 25 o *RequestOptions 26 fw *imock.MockFramework 27 err error 28 ) 29 30 BeforeEach(func() { 31 mockctl = gomock.NewController(GinkgoT()) 32 fw, _ = imock.NewFrameworkForTests(mockctl, GinkgoWriter, imock.WithCallerID()) 33 34 ddl, _ := agent.FindLocally("package", []string{"testdata"}) 35 o, err = NewRequestOptions(fw, ddl) 36 Expect(err).ToNot(HaveOccurred()) 37 }) 38 39 AfterEach(func() { 40 mockctl.Finish() 41 }) 42 43 Describe("ConfigureMessage", func() { 44 It("Should configure the message", func() { 45 msg, err := message.NewMessage(nil, "test", "mcollective", "request", nil, fw) 46 Expect(err).ToNot(HaveOccurred()) 47 48 Targets([]string{"host1", "host2"})(o) 49 BroadcastRequest()(o) 50 51 err = o.ConfigureMessage(msg) 52 Expect(err).ToNot(HaveOccurred()) 53 54 Expect(msg.DiscoveredHosts()).To(Equal([]string{"host1", "host2"})) 55 Expect(o.Targets).To(Equal([]string{"host1", "host2"})) 56 Expect(msg.Type()).To(Equal("request")) 57 Expect(o.ReplyTo).To(Equal(msg.ReplyTo())) 58 Expect(o.ProcessReplies).To(BeTrue()) 59 Expect(o.totalStats.discoveredNodes).To(Equal([]string{"host1", "host2"})) 60 Expect(o.totalStats.RequestID).To(Equal(msg.RequestID())) 61 Expect(o.RequestID).To(Equal(msg.RequestID())) 62 }) 63 64 It("Should support the message supplying targets", func() { 65 msg, err := message.NewMessage(nil, "test", "mcollective", "request", nil, fw) 66 Expect(err).ToNot(HaveOccurred()) 67 msg.SetDiscoveredHosts([]string{"host1", "host2"}) 68 69 o.ConfigureMessage(msg) 70 71 Expect(o.Targets).To(Equal([]string{"host1", "host2"})) 72 }) 73 74 It("Should support custom reply targets", func() { 75 msg, err := message.NewMessage(nil, "test", "mcollective", "request", nil, fw) 76 Expect(err).ToNot(HaveOccurred()) 77 78 Targets([]string{"host1", "host2"})(o) 79 ReplyTo("test.target")(o) 80 81 o.ConfigureMessage(msg) 82 83 Expect(msg.ReplyTo()).To(Equal("test.target")) 84 Expect(o.ReplyTo).To(Equal(msg.ReplyTo())) 85 Expect(o.ProcessReplies).To(BeFalse()) 86 }) 87 88 It("Should support limiting targets", func() { 89 targets := make([]string, 100) 90 for i := 0; i < 100; i++ { 91 targets[i] = fmt.Sprintf("target%d", i) 92 } 93 94 msg, err := message.NewMessage(nil, "test", "mcollective", "request", nil, fw) 95 Expect(err).ToNot(HaveOccurred()) 96 97 Targets(targets)(o) 98 LimitMethod("first")(o) 99 LimitSize("2")(o) 100 o.ConfigureMessage(msg) 101 Expect(o.Targets).To(Equal([]string{"target0", "target1"})) 102 Expect(o.totalStats.discoveredNodes).To(Equal([]string{"target0", "target1"})) 103 }) 104 105 It("Should support cached transports", func() { 106 targets := make([]string, 100) 107 for i := 0; i < 100; i++ { 108 targets[i] = fmt.Sprintf("target%d", i) 109 } 110 111 msg, err := message.NewMessage(nil, "test", "mcollective", "request", nil, fw) 112 Expect(err).ToNot(HaveOccurred()) 113 114 msg.CacheTransport() 115 Targets(targets)(o) 116 InBatches(10, 30)(o) 117 DiscoveryTimeout(2 * time.Second)(o) 118 Timeout(20 * time.Second)(o) 119 msg.SetTTL(10) 120 121 err = o.ConfigureMessage(msg) 122 Expect(err).ToNot(HaveOccurred()) 123 124 expected := 10 * (10 + 2 + 20) 125 Expect(msg.TTL()).To(Equal(expected)) 126 }) 127 128 It("Should limit cached TTL to 5 hours", func() { 129 targets := make([]string, 100) 130 for i := 0; i < 100; i++ { 131 targets[i] = fmt.Sprintf("target%d", i) 132 } 133 134 msg, err := message.NewMessage(nil, "test", "mcollective", "request", nil, fw) 135 Expect(err).ToNot(HaveOccurred()) 136 137 msg.CacheTransport() 138 Targets(targets)(o) 139 InBatches(10, 30)(o) 140 DiscoveryTimeout(2 * time.Second)(o) 141 Timeout(20 * time.Second)(o) 142 msg.SetTTL(int(6 * time.Hour.Seconds())) 143 144 err = o.ConfigureMessage(msg) 145 Expect(err).To(MatchError("cached transport TTL is unreasonably long")) 146 }) 147 148 It("Should support service requests", func() { 149 msg, err := message.NewMessage(nil, "test", "mcollective", "request", nil, fw) 150 Expect(err).ToNot(HaveOccurred()) 151 152 msg.CacheTransport() 153 ServiceRequest()(o) 154 err = o.ConfigureMessage(msg) 155 Expect(err).ToNot(HaveOccurred()) 156 157 Expect(msg.Type()).To(Equal(inter.ServiceRequestMessageType)) 158 Expect(msg.Filter().Empty()).To(BeTrue()) 159 Expect(msg.DiscoveredHosts()).To(BeEmpty()) 160 }) 161 }) 162 163 Describe("NewRequestOptions", func() { 164 It("Should create correct new options", func() { 165 Expect(o.ProtocolVersion).To(Equal(protocol.RequestV1)) 166 Expect(o.RequestType).To(Equal("direct_request")) 167 Expect(o.Collective).To(Equal("ginkgo")) 168 Expect(o.ProcessReplies).To(BeTrue()) 169 Expect(o.Timeout).To(Equal(time.Duration(182) * time.Second)) 170 Expect(o.fw).To(Equal(fw)) 171 Expect(o.LimitSeed).To(BeNumerically(">", 0)) 172 Expect(o.LimitMethod).To(Equal("first")) 173 }) 174 }) 175 176 Describe("Stats", func() { 177 It("Should return the stats", func() { 178 Expect(o.Stats()).To(Equal(o.totalStats)) 179 }) 180 }) 181 182 Describe("Targets", func() { 183 It("Should set the targets", func() { 184 Targets([]string{"host1"})(o) 185 Expect(o.Targets).To(Equal([]string{"host1"})) 186 }) 187 }) 188 189 Describe("Protocol", func() { 190 It("Should set the protocol to use", func() { 191 Protocol(protocol.RequestV1)(o) 192 Expect(o.ProtocolVersion).To(Equal(protocol.RequestV1)) 193 }) 194 }) 195 196 Describe("DirectRequest", func() { 197 It("Should set the type", func() { 198 DirectRequest()(o) 199 Expect(o.RequestType).To(Equal("direct_request")) 200 }) 201 }) 202 203 Describe("BroadcastRequest", func() { 204 It("Should set the type", func() { 205 BroadcastRequest()(o) 206 Expect(o.RequestType).To(Equal("request")) 207 }) 208 }) 209 210 Describe("Workers", func() { 211 It("Should set the workers", func() { 212 Workers(10)(o) 213 Expect(o.Workers).To(Equal(10)) 214 }) 215 }) 216 217 Describe("Collective", func() { 218 It("Should set the collective", func() { 219 Collective("bob")(o) 220 Expect(o.Collective).To(Equal("bob")) 221 }) 222 }) 223 224 Describe("ReplyTo", func() { 225 It("Should set the target", func() { 226 ReplyTo("bob")(o) 227 Expect(o.ReplyTo).To(Equal("bob")) 228 }) 229 }) 230 231 Describe("InBatches", func() { 232 It("Should set the size, batched mode and sleep", func() { 233 InBatches(10, 5)(o) 234 Expect(o.BatchSize).To(Equal(10)) 235 Expect(o.BatchSleep).To(Equal(5 * time.Second)) 236 }) 237 }) 238 239 Describe("Replies", func() { 240 It("Should set the channel and disable the handlers", func() { 241 Replies(make(chan inter.ConnectorMessage, 123))(o) 242 Expect(o.Replies).To(HaveCap(123)) 243 }) 244 }) 245 246 Describe("Timeout", func() { 247 It("Should set the timeout", func() { 248 Timeout(10 * time.Second)(o) 249 Expect(o.Timeout).To(Equal(10 * time.Second)) 250 }) 251 }) 252 253 Describe("ReplyHandler", func() { 254 It("Should set the handler", func() { 255 seen := false 256 257 ReplyHandler(func(p protocol.Reply, r *RPCReply) { seen = true })(o) 258 259 o.Handler(nil, nil) 260 261 Expect(seen).To(BeTrue()) 262 }) 263 }) 264 265 Describe("ConnectionName", func() { 266 It("Should set the name", func() { 267 ConnectionName("ginkgo")(o) 268 Expect(o.ConnectionName).To(Equal("ginkgo")) 269 }) 270 }) 271 272 Describe("LimitMethod", func() { 273 It("Should set the method", func() { 274 LimitMethod("random")(o) 275 Expect(o.LimitMethod).To(Equal("random")) 276 }) 277 }) 278 279 Describe("LimitSize", func() { 280 It("Should set the size", func() { 281 LimitSize("10%")(o) 282 Expect(o.LimitSize).To(Equal("10%")) 283 }) 284 }) 285 286 Describe("DiscoveryStartCB", func() { 287 It("Should set the cb", func() { 288 called := false 289 cb := func() { called = true } 290 DiscoveryStartCB(cb)(o) 291 o.DiscoveryStartCB() 292 Expect(called).To(BeTrue()) 293 }) 294 }) 295 296 Describe("DiscoveryStartCB", func() { 297 It("Should set the cb", func() { 298 called := false 299 cb := func(_, _ int) error { called = true; return nil } 300 301 DiscoveryEndCB(cb)(o) 302 o.DiscoveryEndCB(0, 0) 303 304 Expect(called).To(BeTrue()) 305 }) 306 }) 307 308 Describe("LimitSeed", func() { 309 It("Should set the seed", func() { 310 LimitSeed(100)(o) 311 Expect(o.LimitSeed).To(Equal((int64(100)))) 312 }) 313 }) 314 315 Describe("limitTargets", func() { 316 var targets []string 317 318 BeforeEach(func() { 319 targets = make([]string, 100) 320 for i := 0; i < 100; i++ { 321 targets[i] = fmt.Sprintf("target%d", i) 322 } 323 }) 324 325 It("Should not limit to 0", func() { 326 o.LimitSize = "0" 327 _, err := o.limitTargets(targets) 328 Expect(err).To(MatchError("no targets left after applying target limits of '0'")) 329 }) 330 331 It("Should accept only valid methods", func() { 332 o.LimitMethod = "broken" 333 l, err := o.limitTargets(targets) 334 Expect(err).To(MatchError("limit method 'broken' is not valid, only 'random' or 'first' supported")) 335 Expect(l).To(HaveLen(100)) 336 }) 337 338 It("Should return the supplied targets unshuffled when limit size is not set", func() { 339 o.LimitSize = "" 340 o.LimitMethod = "random" 341 l, err := o.limitTargets(targets) 342 Expect(err).ToNot(HaveOccurred()) 343 Expect(l).To(HaveLen(100)) 344 Expect(targets[0]).To(Equal("target0")) 345 Expect(targets[20]).To(Equal("target20")) 346 Expect(targets[30]).To(Equal("target30")) 347 Expect(targets[40]).To(Equal("target40")) 348 Expect(targets[50]).To(Equal("target50")) 349 Expect(targets[99]).To(Equal("target99")) 350 }) 351 352 It("Should limit to specific size and optionally shuffle the targets", func() { 353 o.LimitSize = "5" 354 o.LimitMethod = "first" 355 l, err := o.limitTargets(targets) 356 Expect(err).ToNot(HaveOccurred()) 357 Expect(l).To(HaveLen(5)) 358 Expect(l).To(Equal([]string{"target0", "target1", "target2", "target3", "target4"})) 359 360 o.LimitMethod = "random" 361 o.LimitSeed = 1 362 l, err = o.limitTargets(targets) 363 Expect(err).ToNot(HaveOccurred()) 364 Expect(l).To(HaveLen(5)) 365 Expect(l).To(Equal([]string{"target19", "target26", "target0", "target73", "target94"})) 366 }) 367 368 It("Should limit to specific percentage and optionally shuffle the targets", func() { 369 o.LimitSize = "5%" 370 o.LimitMethod = "first" 371 l, err := o.limitTargets(targets) 372 Expect(err).ToNot(HaveOccurred()) 373 Expect(l).To(HaveLen(5)) 374 Expect(l).To(Equal(targets[0:5])) 375 376 o.LimitMethod = "random" 377 o.LimitSeed = 1 378 l, err = o.limitTargets(targets) 379 Expect(err).ToNot(HaveOccurred()) 380 Expect(l).To(HaveLen(5)) 381 Expect(l).To(Equal([]string{"target19", "target26", "target0", "target73", "target94"})) 382 }) 383 }) 384 385 Describe("shuffleLimitedTargets", func() { 386 var targets []string 387 388 BeforeEach(func() { 389 targets = make([]string, 100) 390 for i := 0; i < 100; i++ { 391 targets[i] = fmt.Sprintf("target%d", i) 392 } 393 }) 394 395 It("Should support not shuffling non random method targets", func() { 396 o.LimitMethod = "first" 397 o.shuffleLimitedTargets(targets) 398 Expect(targets).To(HaveLen(100)) 399 Expect(targets[0]).To(Equal("target0")) 400 Expect(targets[20]).To(Equal("target20")) 401 Expect(targets[30]).To(Equal("target30")) 402 Expect(targets[40]).To(Equal("target40")) 403 Expect(targets[50]).To(Equal("target50")) 404 Expect(targets[99]).To(Equal("target99")) 405 }) 406 407 It("Should shuffle random method targets", FlakeAttempts(3), func() { 408 o.LimitMethod = "random" 409 o.LimitSeed = -1 410 o.shuffleLimitedTargets(targets) 411 Expect(targets).To(HaveLen(100)) 412 // small chance of failure here if random shuffling leaves these 2 in place 413 for i := 0; i < 10; i++ { 414 if targets[0] == "target0" || targets[99] == "target99" { 415 o.shuffleLimitedTargets(targets) 416 } else { 417 break 418 } 419 } 420 Expect(targets[0]).ToNot(Equal("target0")) 421 Expect(targets[99]).ToNot(Equal("target99")) 422 Expect(targets).To(HaveLen(100)) 423 }) 424 425 It("Should support seeds", func() { 426 o.LimitMethod = "random" 427 o.LimitSeed = 1 428 o.shuffleLimitedTargets(targets) 429 Expect(targets).To(HaveLen(100)) 430 Expect(targets[0]).To(Equal("target19")) 431 Expect(targets[1]).To(Equal("target26")) 432 Expect(targets[2]).To(Equal("target0")) 433 Expect(targets[3]).To(Equal("target73")) 434 Expect(targets).To(HaveLen(100)) 435 }) 436 }) 437 })