github.com/nsqio/nsq@v1.3.0/internal/clusterinfo/data.go (about) 1 package clusterinfo 2 3 import ( 4 "fmt" 5 "net" 6 "net/url" 7 "sort" 8 "strconv" 9 "strings" 10 "sync" 11 12 "github.com/blang/semver" 13 "github.com/nsqio/nsq/internal/http_api" 14 "github.com/nsqio/nsq/internal/lg" 15 "github.com/nsqio/nsq/internal/stringy" 16 ) 17 18 type PartialErr interface { 19 error 20 Errors() []error 21 } 22 23 type ErrList []error 24 25 func (l ErrList) Error() string { 26 var es []string 27 for _, e := range l { 28 es = append(es, e.Error()) 29 } 30 return strings.Join(es, "\n") 31 } 32 33 func (l ErrList) Errors() []error { 34 return l 35 } 36 37 type ClusterInfo struct { 38 log lg.AppLogFunc 39 client *http_api.Client 40 } 41 42 func New(log lg.AppLogFunc, client *http_api.Client) *ClusterInfo { 43 return &ClusterInfo{ 44 log: log, 45 client: client, 46 } 47 } 48 49 func (c *ClusterInfo) logf(f string, args ...interface{}) { 50 if c.log != nil { 51 c.log(lg.INFO, f, args...) 52 } 53 } 54 55 // GetVersion returns a semver.Version object by querying /info 56 func (c *ClusterInfo) GetVersion(addr string) (semver.Version, error) { 57 endpoint := fmt.Sprintf("http://%s/info", addr) 58 var resp struct { 59 Version string `json:"version"` 60 } 61 err := c.client.GETV1(endpoint, &resp) 62 if err != nil { 63 return semver.Version{}, err 64 } 65 if resp.Version == "" { 66 resp.Version = "unknown" 67 } 68 return semver.Parse(resp.Version) 69 } 70 71 // GetLookupdTopics returns a []string containing a union of all the topics 72 // from all the given nsqlookupd 73 func (c *ClusterInfo) GetLookupdTopics(lookupdHTTPAddrs []string) ([]string, error) { 74 var topics []string 75 var lock sync.Mutex 76 var wg sync.WaitGroup 77 var errs []error 78 79 type respType struct { 80 Topics []string `json:"topics"` 81 } 82 83 for _, addr := range lookupdHTTPAddrs { 84 wg.Add(1) 85 go func(addr string) { 86 defer wg.Done() 87 88 endpoint := fmt.Sprintf("http://%s/topics", addr) 89 c.logf("CI: querying nsqlookupd %s", endpoint) 90 91 var resp respType 92 err := c.client.GETV1(endpoint, &resp) 93 if err != nil { 94 lock.Lock() 95 errs = append(errs, err) 96 lock.Unlock() 97 return 98 } 99 100 lock.Lock() 101 defer lock.Unlock() 102 topics = append(topics, resp.Topics...) 103 }(addr) 104 } 105 wg.Wait() 106 107 if len(errs) == len(lookupdHTTPAddrs) { 108 return nil, fmt.Errorf("failed to query any nsqlookupd: %s", ErrList(errs)) 109 } 110 111 topics = stringy.Uniq(topics) 112 sort.Strings(topics) 113 114 if len(errs) > 0 { 115 return topics, ErrList(errs) 116 } 117 return topics, nil 118 } 119 120 // GetLookupdTopicChannels returns a []string containing a union of all the channels 121 // from all the given lookupd for the given topic 122 func (c *ClusterInfo) GetLookupdTopicChannels(topic string, lookupdHTTPAddrs []string) ([]string, error) { 123 var channels []string 124 var lock sync.Mutex 125 var wg sync.WaitGroup 126 var errs []error 127 128 type respType struct { 129 Channels []string `json:"channels"` 130 } 131 132 for _, addr := range lookupdHTTPAddrs { 133 wg.Add(1) 134 go func(addr string) { 135 defer wg.Done() 136 137 endpoint := fmt.Sprintf("http://%s/channels?topic=%s", addr, url.QueryEscape(topic)) 138 c.logf("CI: querying nsqlookupd %s", endpoint) 139 140 var resp respType 141 err := c.client.GETV1(endpoint, &resp) 142 if err != nil { 143 lock.Lock() 144 errs = append(errs, err) 145 lock.Unlock() 146 return 147 } 148 149 lock.Lock() 150 defer lock.Unlock() 151 channels = append(channels, resp.Channels...) 152 }(addr) 153 } 154 wg.Wait() 155 156 if len(errs) == len(lookupdHTTPAddrs) { 157 return nil, fmt.Errorf("failed to query any nsqlookupd: %s", ErrList(errs)) 158 } 159 160 channels = stringy.Uniq(channels) 161 sort.Strings(channels) 162 163 if len(errs) > 0 { 164 return channels, ErrList(errs) 165 } 166 return channels, nil 167 } 168 169 // GetLookupdProducers returns Producers of all the nsqd connected to the given lookupds 170 func (c *ClusterInfo) GetLookupdProducers(lookupdHTTPAddrs []string) (Producers, error) { 171 var producers []*Producer 172 var lock sync.Mutex 173 var wg sync.WaitGroup 174 var errs []error 175 176 producersByAddr := make(map[string]*Producer) 177 maxVersion, _ := semver.Parse("0.0.0") 178 179 type respType struct { 180 Producers []*Producer `json:"producers"` 181 } 182 183 for _, addr := range lookupdHTTPAddrs { 184 wg.Add(1) 185 go func(addr string) { 186 defer wg.Done() 187 188 endpoint := fmt.Sprintf("http://%s/nodes", addr) 189 c.logf("CI: querying nsqlookupd %s", endpoint) 190 191 var resp respType 192 err := c.client.GETV1(endpoint, &resp) 193 if err != nil { 194 lock.Lock() 195 errs = append(errs, err) 196 lock.Unlock() 197 return 198 } 199 200 lock.Lock() 201 defer lock.Unlock() 202 for _, producer := range resp.Producers { 203 key := producer.TCPAddress() 204 p, ok := producersByAddr[key] 205 if !ok { 206 producersByAddr[key] = producer 207 producers = append(producers, producer) 208 if maxVersion.LT(producer.VersionObj) { 209 maxVersion = producer.VersionObj 210 } 211 sort.Sort(producer.Topics) 212 p = producer 213 } 214 p.RemoteAddresses = append(p.RemoteAddresses, 215 fmt.Sprintf("%s/%s", addr, producer.Address())) 216 } 217 }(addr) 218 } 219 wg.Wait() 220 221 if len(errs) == len(lookupdHTTPAddrs) { 222 return nil, fmt.Errorf("failed to query any nsqlookupd: %s", ErrList(errs)) 223 } 224 225 for _, producer := range producersByAddr { 226 if producer.VersionObj.LT(maxVersion) { 227 producer.OutOfDate = true 228 } 229 } 230 sort.Sort(ProducersByHost{producers}) 231 232 if len(errs) > 0 { 233 return producers, ErrList(errs) 234 } 235 return producers, nil 236 } 237 238 // GetLookupdTopicProducers returns Producers of all the nsqd for a given topic by 239 // unioning the nodes returned from the given lookupd 240 func (c *ClusterInfo) GetLookupdTopicProducers(topic string, lookupdHTTPAddrs []string) (Producers, error) { 241 var producers Producers 242 var lock sync.Mutex 243 var wg sync.WaitGroup 244 var errs []error 245 246 type respType struct { 247 Producers Producers `json:"producers"` 248 } 249 250 for _, addr := range lookupdHTTPAddrs { 251 wg.Add(1) 252 go func(addr string) { 253 defer wg.Done() 254 255 endpoint := fmt.Sprintf("http://%s/lookup?topic=%s", addr, url.QueryEscape(topic)) 256 c.logf("CI: querying nsqlookupd %s", endpoint) 257 258 var resp respType 259 err := c.client.GETV1(endpoint, &resp) 260 if err != nil { 261 lock.Lock() 262 errs = append(errs, err) 263 lock.Unlock() 264 return 265 } 266 267 lock.Lock() 268 defer lock.Unlock() 269 for _, p := range resp.Producers { 270 for _, pp := range producers { 271 if p.HTTPAddress() == pp.HTTPAddress() { 272 goto skip 273 } 274 } 275 producers = append(producers, p) 276 skip: 277 } 278 }(addr) 279 } 280 wg.Wait() 281 282 if len(errs) == len(lookupdHTTPAddrs) { 283 return nil, fmt.Errorf("failed to query any nsqlookupd: %s", ErrList(errs)) 284 } 285 if len(errs) > 0 { 286 return producers, ErrList(errs) 287 } 288 return producers, nil 289 } 290 291 // GetNSQDTopics returns a []string containing all the topics produced by the given nsqd 292 func (c *ClusterInfo) GetNSQDTopics(nsqdHTTPAddrs []string) ([]string, error) { 293 var topics []string 294 var lock sync.Mutex 295 var wg sync.WaitGroup 296 var errs []error 297 298 type respType struct { 299 Topics []struct { 300 Name string `json:"topic_name"` 301 } `json:"topics"` 302 } 303 304 for _, addr := range nsqdHTTPAddrs { 305 wg.Add(1) 306 go func(addr string) { 307 defer wg.Done() 308 309 endpoint := fmt.Sprintf("http://%s/stats?format=json", addr) 310 c.logf("CI: querying nsqd %s", endpoint) 311 312 var resp respType 313 err := c.client.GETV1(endpoint, &resp) 314 if err != nil { 315 lock.Lock() 316 errs = append(errs, err) 317 lock.Unlock() 318 return 319 } 320 321 lock.Lock() 322 defer lock.Unlock() 323 for _, topic := range resp.Topics { 324 topics = stringy.Add(topics, topic.Name) 325 } 326 }(addr) 327 } 328 wg.Wait() 329 330 if len(errs) == len(nsqdHTTPAddrs) { 331 return nil, fmt.Errorf("failed to query any nsqd: %s", ErrList(errs)) 332 } 333 334 sort.Strings(topics) 335 336 if len(errs) > 0 { 337 return topics, ErrList(errs) 338 } 339 return topics, nil 340 } 341 342 // GetNSQDProducers returns Producers of all the given nsqd 343 func (c *ClusterInfo) GetNSQDProducers(nsqdHTTPAddrs []string) (Producers, error) { 344 var producers Producers 345 var lock sync.Mutex 346 var wg sync.WaitGroup 347 var errs []error 348 349 type infoRespType struct { 350 Version string `json:"version"` 351 BroadcastAddress string `json:"broadcast_address"` 352 Hostname string `json:"hostname"` 353 HTTPPort int `json:"http_port"` 354 TCPPort int `json:"tcp_port"` 355 } 356 357 type statsRespType struct { 358 Topics []struct { 359 Name string `json:"topic_name"` 360 } `json:"topics"` 361 } 362 363 for _, addr := range nsqdHTTPAddrs { 364 wg.Add(1) 365 go func(addr string) { 366 defer wg.Done() 367 368 endpoint := fmt.Sprintf("http://%s/info", addr) 369 c.logf("CI: querying nsqd %s", endpoint) 370 371 var infoResp infoRespType 372 err := c.client.GETV1(endpoint, &infoResp) 373 if err != nil { 374 lock.Lock() 375 errs = append(errs, err) 376 lock.Unlock() 377 return 378 } 379 380 endpoint = fmt.Sprintf("http://%s/stats?format=json&include_clients=false", addr) 381 c.logf("CI: querying nsqd %s", endpoint) 382 383 var statsResp statsRespType 384 err = c.client.GETV1(endpoint, &statsResp) 385 if err != nil { 386 lock.Lock() 387 errs = append(errs, err) 388 lock.Unlock() 389 return 390 } 391 392 var producerTopics ProducerTopics 393 for _, t := range statsResp.Topics { 394 producerTopics = append(producerTopics, ProducerTopic{Topic: t.Name}) 395 } 396 397 version, err := semver.Parse(infoResp.Version) 398 if err != nil { 399 version, _ = semver.Parse("0.0.0") 400 } 401 402 lock.Lock() 403 defer lock.Unlock() 404 producers = append(producers, &Producer{ 405 Version: infoResp.Version, 406 VersionObj: version, 407 BroadcastAddress: infoResp.BroadcastAddress, 408 Hostname: infoResp.Hostname, 409 HTTPPort: infoResp.HTTPPort, 410 TCPPort: infoResp.TCPPort, 411 Topics: producerTopics, 412 }) 413 }(addr) 414 } 415 wg.Wait() 416 417 if len(errs) == len(nsqdHTTPAddrs) { 418 return nil, fmt.Errorf("failed to query any nsqd: %s", ErrList(errs)) 419 } 420 if len(errs) > 0 { 421 return producers, ErrList(errs) 422 } 423 return producers, nil 424 } 425 426 // GetNSQDTopicProducers returns Producers containing the addresses of all the nsqd 427 // that produce the given topic 428 func (c *ClusterInfo) GetNSQDTopicProducers(topic string, nsqdHTTPAddrs []string) (Producers, error) { 429 var producers Producers 430 var lock sync.Mutex 431 var wg sync.WaitGroup 432 var errs []error 433 434 type infoRespType struct { 435 Version string `json:"version"` 436 BroadcastAddress string `json:"broadcast_address"` 437 Hostname string `json:"hostname"` 438 HTTPPort int `json:"http_port"` 439 TCPPort int `json:"tcp_port"` 440 } 441 442 type statsRespType struct { 443 Topics []struct { 444 Name string `json:"topic_name"` 445 } `json:"topics"` 446 } 447 448 for _, addr := range nsqdHTTPAddrs { 449 wg.Add(1) 450 go func(addr string) { 451 defer wg.Done() 452 453 endpoint := fmt.Sprintf("http://%s/stats?format=json&topic=%s&include_clients=false", 454 addr, url.QueryEscape(topic)) 455 c.logf("CI: querying nsqd %s", endpoint) 456 457 var statsResp statsRespType 458 err := c.client.GETV1(endpoint, &statsResp) 459 if err != nil { 460 lock.Lock() 461 errs = append(errs, err) 462 lock.Unlock() 463 return 464 } 465 466 var producerTopics ProducerTopics 467 for _, t := range statsResp.Topics { 468 producerTopics = append(producerTopics, ProducerTopic{Topic: t.Name}) 469 } 470 471 for _, t := range statsResp.Topics { 472 if t.Name == topic { 473 endpoint := fmt.Sprintf("http://%s/info", addr) 474 c.logf("CI: querying nsqd %s", endpoint) 475 476 var infoResp infoRespType 477 err := c.client.GETV1(endpoint, &infoResp) 478 if err != nil { 479 lock.Lock() 480 errs = append(errs, err) 481 lock.Unlock() 482 return 483 } 484 485 version, err := semver.Parse(infoResp.Version) 486 if err != nil { 487 version, _ = semver.Parse("0.0.0") 488 } 489 490 // if BroadcastAddress/HTTPPort are missing, use the values from `addr` for 491 // backwards compatibility 492 493 if infoResp.BroadcastAddress == "" { 494 var p string 495 infoResp.BroadcastAddress, p, _ = net.SplitHostPort(addr) 496 infoResp.HTTPPort, _ = strconv.Atoi(p) 497 } 498 if infoResp.Hostname == "" { 499 infoResp.Hostname, _, _ = net.SplitHostPort(addr) 500 } 501 502 lock.Lock() 503 producers = append(producers, &Producer{ 504 Version: infoResp.Version, 505 VersionObj: version, 506 BroadcastAddress: infoResp.BroadcastAddress, 507 Hostname: infoResp.Hostname, 508 HTTPPort: infoResp.HTTPPort, 509 TCPPort: infoResp.TCPPort, 510 Topics: producerTopics, 511 }) 512 lock.Unlock() 513 514 return 515 } 516 } 517 }(addr) 518 } 519 wg.Wait() 520 521 if len(errs) == len(nsqdHTTPAddrs) { 522 return nil, fmt.Errorf("failed to query any nsqd: %s", ErrList(errs)) 523 } 524 if len(errs) > 0 { 525 return producers, ErrList(errs) 526 } 527 return producers, nil 528 } 529 530 // GetNSQDStats returns aggregate topic and channel stats from the given Producers 531 // 532 // if selectedChannel is empty, this will return stats for topic/channel 533 // if selectedTopic is empty, this will return stats for *all* topic/channels 534 // if includeClients is false, this will *not* return client stats for channels 535 // and the ChannelStats dict will be keyed by topic + ':' + channel 536 func (c *ClusterInfo) GetNSQDStats(producers Producers, 537 selectedTopic string, selectedChannel string, 538 includeClients bool) ([]*TopicStats, map[string]*ChannelStats, error) { 539 var lock sync.Mutex 540 var wg sync.WaitGroup 541 var topicStatsList TopicStatsList 542 var errs []error 543 544 channelStatsMap := make(map[string]*ChannelStats) 545 546 type respType struct { 547 Topics []*TopicStats `json:"topics"` 548 } 549 550 for _, p := range producers { 551 wg.Add(1) 552 go func(p *Producer) { 553 defer wg.Done() 554 555 addr := p.HTTPAddress() 556 557 endpoint := fmt.Sprintf("http://%s/stats?format=json", addr) 558 if selectedTopic != "" { 559 endpoint += "&topic=" + url.QueryEscape(selectedTopic) 560 if selectedChannel != "" { 561 endpoint += "&channel=" + url.QueryEscape(selectedChannel) 562 } 563 } 564 if !includeClients { 565 endpoint += "&include_clients=false" 566 } 567 568 c.logf("CI: querying nsqd %s", endpoint) 569 570 var resp respType 571 err := c.client.GETV1(endpoint, &resp) 572 if err != nil { 573 lock.Lock() 574 errs = append(errs, err) 575 lock.Unlock() 576 return 577 } 578 579 lock.Lock() 580 defer lock.Unlock() 581 for _, topic := range resp.Topics { 582 topic.Node = addr 583 topic.Hostname = p.Hostname 584 topic.MemoryDepth = topic.Depth - topic.BackendDepth 585 if selectedTopic != "" && topic.TopicName != selectedTopic { 586 continue 587 } 588 topicStatsList = append(topicStatsList, topic) 589 590 for _, channel := range topic.Channels { 591 channel.Node = addr 592 channel.Hostname = p.Hostname 593 channel.TopicName = topic.TopicName 594 channel.MemoryDepth = channel.Depth - channel.BackendDepth 595 key := channel.ChannelName 596 if selectedTopic == "" { 597 key = fmt.Sprintf("%s:%s", topic.TopicName, channel.ChannelName) 598 } 599 channelStats, ok := channelStatsMap[key] 600 if !ok { 601 channelStats = &ChannelStats{ 602 Node: addr, 603 TopicName: topic.TopicName, 604 ChannelName: channel.ChannelName, 605 } 606 channelStatsMap[key] = channelStats 607 } 608 for _, c := range channel.Clients { 609 c.Node = addr 610 } 611 channelStats.Add(channel) 612 } 613 } 614 }(p) 615 } 616 wg.Wait() 617 618 if len(errs) == len(producers) { 619 return nil, nil, fmt.Errorf("failed to query any nsqd: %s", ErrList(errs)) 620 } 621 622 sort.Sort(TopicStatsByHost{topicStatsList}) 623 624 if len(errs) > 0 { 625 return topicStatsList, channelStatsMap, ErrList(errs) 626 } 627 return topicStatsList, channelStatsMap, nil 628 } 629 630 // TombstoneNodeForTopic tombstones the given node for the given topic on all the given nsqlookupd 631 // and deletes the topic from the node 632 func (c *ClusterInfo) TombstoneNodeForTopic(topic string, node string, lookupdHTTPAddrs []string) error { 633 var errs []error 634 635 // tombstone the topic on all the lookupds 636 qs := fmt.Sprintf("topic=%s&node=%s", url.QueryEscape(topic), url.QueryEscape(node)) 637 err := c.nsqlookupdPOST(lookupdHTTPAddrs, "topic/tombstone", qs) 638 if err != nil { 639 pe, ok := err.(PartialErr) 640 if !ok { 641 return err 642 } 643 errs = append(errs, pe.Errors()...) 644 } 645 646 producers, err := c.GetNSQDProducers([]string{node}) 647 if err != nil { 648 pe, ok := err.(PartialErr) 649 if !ok { 650 return err 651 } 652 errs = append(errs, pe.Errors()...) 653 } 654 655 // delete the topic on the producer 656 qs = fmt.Sprintf("topic=%s", url.QueryEscape(topic)) 657 err = c.producersPOST(producers, "topic/delete", qs) 658 if err != nil { 659 pe, ok := err.(PartialErr) 660 if !ok { 661 return err 662 } 663 errs = append(errs, pe.Errors()...) 664 } 665 666 if len(errs) > 0 { 667 return ErrList(errs) 668 } 669 return nil 670 } 671 672 func (c *ClusterInfo) CreateTopicChannel(topicName string, channelName string, lookupdHTTPAddrs []string) error { 673 var errs []error 674 675 // create the topic on all the nsqlookupd 676 qs := fmt.Sprintf("topic=%s", url.QueryEscape(topicName)) 677 err := c.nsqlookupdPOST(lookupdHTTPAddrs, "topic/create", qs) 678 if err != nil { 679 pe, ok := err.(PartialErr) 680 if !ok { 681 return err 682 } 683 errs = append(errs, pe.Errors()...) 684 } 685 686 if len(channelName) > 0 { 687 qs := fmt.Sprintf("topic=%s&channel=%s", url.QueryEscape(topicName), url.QueryEscape(channelName)) 688 689 // create the channel on all the nsqlookupd 690 err := c.nsqlookupdPOST(lookupdHTTPAddrs, "channel/create", qs) 691 if err != nil { 692 pe, ok := err.(PartialErr) 693 if !ok { 694 return err 695 } 696 errs = append(errs, pe.Errors()...) 697 } 698 699 // create the channel on all the nsqd that produce the topic 700 producers, err := c.GetLookupdTopicProducers(topicName, lookupdHTTPAddrs) 701 if err != nil { 702 pe, ok := err.(PartialErr) 703 if !ok { 704 return err 705 } 706 errs = append(errs, pe.Errors()...) 707 } 708 err = c.producersPOST(producers, "channel/create", qs) 709 if err != nil { 710 pe, ok := err.(PartialErr) 711 if !ok { 712 return err 713 } 714 errs = append(errs, pe.Errors()...) 715 } 716 } 717 718 if len(errs) > 0 { 719 return ErrList(errs) 720 } 721 return nil 722 } 723 724 func (c *ClusterInfo) DeleteTopic(topicName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) error { 725 var errs []error 726 727 // for topic removal, you need to get all the producers _first_ 728 producers, err := c.GetTopicProducers(topicName, lookupdHTTPAddrs, nsqdHTTPAddrs) 729 if err != nil { 730 pe, ok := err.(PartialErr) 731 if !ok { 732 return err 733 } 734 errs = append(errs, pe.Errors()...) 735 } 736 737 qs := fmt.Sprintf("topic=%s", url.QueryEscape(topicName)) 738 739 // remove the topic from all the nsqlookupd 740 err = c.nsqlookupdPOST(lookupdHTTPAddrs, "topic/delete", qs) 741 if err != nil { 742 pe, ok := err.(PartialErr) 743 if !ok { 744 return err 745 } 746 errs = append(errs, pe.Errors()...) 747 } 748 749 // remove the topic from all the nsqd that produce this topic 750 err = c.producersPOST(producers, "topic/delete", qs) 751 if err != nil { 752 pe, ok := err.(PartialErr) 753 if !ok { 754 return err 755 } 756 errs = append(errs, pe.Errors()...) 757 } 758 759 if len(errs) > 0 { 760 return ErrList(errs) 761 } 762 return nil 763 } 764 765 func (c *ClusterInfo) DeleteChannel(topicName string, channelName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) error { 766 var errs []error 767 768 producers, err := c.GetTopicProducers(topicName, lookupdHTTPAddrs, nsqdHTTPAddrs) 769 if err != nil { 770 pe, ok := err.(PartialErr) 771 if !ok { 772 return err 773 } 774 errs = append(errs, pe.Errors()...) 775 } 776 777 qs := fmt.Sprintf("topic=%s&channel=%s", url.QueryEscape(topicName), url.QueryEscape(channelName)) 778 779 // remove the channel from all the nsqlookupd 780 err = c.nsqlookupdPOST(lookupdHTTPAddrs, "channel/delete", qs) 781 if err != nil { 782 pe, ok := err.(PartialErr) 783 if !ok { 784 return err 785 } 786 errs = append(errs, pe.Errors()...) 787 } 788 789 // remove the channel from all the nsqd that produce this topic 790 err = c.producersPOST(producers, "channel/delete", qs) 791 if err != nil { 792 pe, ok := err.(PartialErr) 793 if !ok { 794 return err 795 } 796 errs = append(errs, pe.Errors()...) 797 } 798 799 if len(errs) > 0 { 800 return ErrList(errs) 801 } 802 return nil 803 } 804 805 func (c *ClusterInfo) PauseTopic(topicName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) error { 806 qs := fmt.Sprintf("topic=%s", url.QueryEscape(topicName)) 807 return c.actionHelper(topicName, lookupdHTTPAddrs, nsqdHTTPAddrs, "topic/pause", qs) 808 } 809 810 func (c *ClusterInfo) UnPauseTopic(topicName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) error { 811 qs := fmt.Sprintf("topic=%s", url.QueryEscape(topicName)) 812 return c.actionHelper(topicName, lookupdHTTPAddrs, nsqdHTTPAddrs, "topic/unpause", qs) 813 } 814 815 func (c *ClusterInfo) PauseChannel(topicName string, channelName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) error { 816 qs := fmt.Sprintf("topic=%s&channel=%s", url.QueryEscape(topicName), url.QueryEscape(channelName)) 817 return c.actionHelper(topicName, lookupdHTTPAddrs, nsqdHTTPAddrs, "channel/pause", qs) 818 } 819 820 func (c *ClusterInfo) UnPauseChannel(topicName string, channelName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) error { 821 qs := fmt.Sprintf("topic=%s&channel=%s", url.QueryEscape(topicName), url.QueryEscape(channelName)) 822 return c.actionHelper(topicName, lookupdHTTPAddrs, nsqdHTTPAddrs, "channel/unpause", qs) 823 } 824 825 func (c *ClusterInfo) EmptyTopic(topicName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) error { 826 qs := fmt.Sprintf("topic=%s", url.QueryEscape(topicName)) 827 return c.actionHelper(topicName, lookupdHTTPAddrs, nsqdHTTPAddrs, "topic/empty", qs) 828 } 829 830 func (c *ClusterInfo) EmptyChannel(topicName string, channelName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) error { 831 qs := fmt.Sprintf("topic=%s&channel=%s", url.QueryEscape(topicName), url.QueryEscape(channelName)) 832 return c.actionHelper(topicName, lookupdHTTPAddrs, nsqdHTTPAddrs, "channel/empty", qs) 833 } 834 835 func (c *ClusterInfo) actionHelper(topicName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string, uri string, qs string) error { 836 var errs []error 837 838 producers, err := c.GetTopicProducers(topicName, lookupdHTTPAddrs, nsqdHTTPAddrs) 839 if err != nil { 840 pe, ok := err.(PartialErr) 841 if !ok { 842 return err 843 } 844 errs = append(errs, pe.Errors()...) 845 } 846 847 err = c.producersPOST(producers, uri, qs) 848 if err != nil { 849 pe, ok := err.(PartialErr) 850 if !ok { 851 return err 852 } 853 errs = append(errs, pe.Errors()...) 854 } 855 856 if len(errs) > 0 { 857 return ErrList(errs) 858 } 859 return nil 860 } 861 862 func (c *ClusterInfo) GetProducers(lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) (Producers, error) { 863 if len(lookupdHTTPAddrs) != 0 { 864 return c.GetLookupdProducers(lookupdHTTPAddrs) 865 } 866 return c.GetNSQDProducers(nsqdHTTPAddrs) 867 } 868 869 func (c *ClusterInfo) GetTopicProducers(topicName string, lookupdHTTPAddrs []string, nsqdHTTPAddrs []string) (Producers, error) { 870 if len(lookupdHTTPAddrs) != 0 { 871 return c.GetLookupdTopicProducers(topicName, lookupdHTTPAddrs) 872 } 873 return c.GetNSQDTopicProducers(topicName, nsqdHTTPAddrs) 874 } 875 876 func (c *ClusterInfo) nsqlookupdPOST(addrs []string, uri string, qs string) error { 877 var errs []error 878 for _, addr := range addrs { 879 endpoint := fmt.Sprintf("http://%s/%s?%s", addr, uri, qs) 880 c.logf("CI: querying nsqlookupd %s", endpoint) 881 err := c.client.POSTV1(endpoint) 882 if err != nil { 883 errs = append(errs, err) 884 } 885 } 886 if len(errs) > 0 { 887 return ErrList(errs) 888 } 889 return nil 890 } 891 892 func (c *ClusterInfo) producersPOST(pl Producers, uri string, qs string) error { 893 var errs []error 894 for _, p := range pl { 895 endpoint := fmt.Sprintf("http://%s/%s?%s", p.HTTPAddress(), uri, qs) 896 c.logf("CI: querying nsqd %s", endpoint) 897 err := c.client.POSTV1(endpoint) 898 if err != nil { 899 errs = append(errs, err) 900 } 901 } 902 if len(errs) > 0 { 903 return ErrList(errs) 904 } 905 return nil 906 }