123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- // Copyright 2016 Circonus, Inc. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package checkmgr
- import (
- "fmt"
- "math/rand"
- "net"
- "net/url"
- "reflect"
- "strconv"
- "strings"
- "time"
- "github.com/circonus-labs/circonus-gometrics/api"
- )
- func init() {
- rand.Seed(time.Now().UnixNano())
- }
- // Get Broker to use when creating a check
- func (cm *CheckManager) getBroker() (*api.Broker, error) {
- if cm.brokerID != 0 {
- cid := fmt.Sprintf("/broker/%d", cm.brokerID)
- broker, err := cm.apih.FetchBroker(api.CIDType(&cid))
- if err != nil {
- return nil, err
- }
- if !cm.isValidBroker(broker) {
- return nil, fmt.Errorf(
- "[ERROR] designated broker %d [%s] is invalid (not active, does not support required check type, or connectivity issue)",
- cm.brokerID,
- broker.Name)
- }
- return broker, nil
- }
- broker, err := cm.selectBroker()
- if err != nil {
- return nil, fmt.Errorf("[ERROR] Unable to fetch suitable broker %s", err)
- }
- return broker, nil
- }
- // Get CN of Broker associated with submission_url to satisfy no IP SANS in certs
- func (cm *CheckManager) getBrokerCN(broker *api.Broker, submissionURL api.URLType) (string, error) {
- u, err := url.Parse(string(submissionURL))
- if err != nil {
- return "", err
- }
- hostParts := strings.Split(u.Host, ":")
- host := hostParts[0]
- if net.ParseIP(host) == nil { // it's a non-ip string
- return u.Host, nil
- }
- cn := ""
- for _, detail := range broker.Details {
- if *detail.IP == host {
- cn = detail.CN
- break
- }
- }
- if cn == "" {
- return "", fmt.Errorf("[ERROR] Unable to match URL host (%s) to Broker", u.Host)
- }
- return cn, nil
- }
- // Select a broker for use when creating a check, if a specific broker
- // was not specified.
- func (cm *CheckManager) selectBroker() (*api.Broker, error) {
- var brokerList *[]api.Broker
- var err error
- enterpriseType := "enterprise"
- if len(cm.brokerSelectTag) > 0 {
- filter := api.SearchFilterType{
- "f__tags_has": cm.brokerSelectTag,
- }
- brokerList, err = cm.apih.SearchBrokers(nil, &filter)
- if err != nil {
- return nil, err
- }
- } else {
- brokerList, err = cm.apih.FetchBrokers()
- if err != nil {
- return nil, err
- }
- }
- if len(*brokerList) == 0 {
- return nil, fmt.Errorf("zero brokers found")
- }
- validBrokers := make(map[string]api.Broker)
- haveEnterprise := false
- for _, broker := range *brokerList {
- broker := broker
- if cm.isValidBroker(&broker) {
- validBrokers[broker.CID] = broker
- if broker.Type == enterpriseType {
- haveEnterprise = true
- }
- }
- }
- if haveEnterprise { // eliminate non-enterprise brokers from valid brokers
- for k, v := range validBrokers {
- if v.Type != enterpriseType {
- delete(validBrokers, k)
- }
- }
- }
- if len(validBrokers) == 0 {
- return nil, fmt.Errorf("found %d broker(s), zero are valid", len(*brokerList))
- }
- validBrokerKeys := reflect.ValueOf(validBrokers).MapKeys()
- selectedBroker := validBrokers[validBrokerKeys[rand.Intn(len(validBrokerKeys))].String()]
- if cm.Debug {
- cm.Log.Printf("[DEBUG] Selected broker '%s'\n", selectedBroker.Name)
- }
- return &selectedBroker, nil
- }
- // Verify broker supports the check type to be used
- func (cm *CheckManager) brokerSupportsCheckType(checkType CheckTypeType, details *api.BrokerDetail) bool {
- baseType := string(checkType)
- for _, module := range details.Modules {
- if module == baseType {
- return true
- }
- }
- if idx := strings.Index(baseType, ":"); idx > 0 {
- baseType = baseType[0:idx]
- }
- for _, module := range details.Modules {
- if module == baseType {
- return true
- }
- }
- return false
- }
- // Is the broker valid (active, supports check type, and reachable)
- func (cm *CheckManager) isValidBroker(broker *api.Broker) bool {
- var brokerHost string
- var brokerPort string
- if broker.Type != "circonus" && broker.Type != "enterprise" {
- return false
- }
- valid := false
- for _, detail := range broker.Details {
- detail := detail
- // broker must be active
- if detail.Status != statusActive {
- if cm.Debug {
- cm.Log.Printf("[DEBUG] Broker '%s' is not active.\n", broker.Name)
- }
- continue
- }
- // broker must have module loaded for the check type to be used
- if !cm.brokerSupportsCheckType(cm.checkType, &detail) {
- if cm.Debug {
- cm.Log.Printf("[DEBUG] Broker '%s' does not support '%s' checks.\n", broker.Name, cm.checkType)
- }
- continue
- }
- if detail.ExternalPort != 0 {
- brokerPort = strconv.Itoa(int(detail.ExternalPort))
- } else {
- if detail.Port != nil && *detail.Port != 0 {
- brokerPort = strconv.Itoa(int(*detail.Port))
- } else {
- brokerPort = "43191"
- }
- }
- if detail.ExternalHost != nil && *detail.ExternalHost != "" {
- brokerHost = *detail.ExternalHost
- } else if detail.IP != nil && *detail.IP != "" {
- brokerHost = *detail.IP
- }
- if brokerHost == "" {
- cm.Log.Printf("[WARN] Broker '%s' instance %s has no IP or external host set", broker.Name, detail.CN)
- continue
- }
- if brokerHost == "trap.noit.circonus.net" && brokerPort != "443" {
- brokerPort = "443"
- }
- retries := 5
- for attempt := 1; attempt <= retries; attempt++ {
- // broker must be reachable and respond within designated time
- conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", brokerHost, brokerPort), cm.brokerMaxResponseTime)
- if err == nil {
- conn.Close()
- valid = true
- break
- }
- cm.Log.Printf("[WARN] Broker '%s' unable to connect, %v. Retrying in 2 seconds, attempt %d of %d.", broker.Name, err, attempt, retries)
- time.Sleep(2 * time.Second)
- }
- if valid {
- if cm.Debug {
- cm.Log.Printf("[DEBUG] Broker '%s' is valid\n", broker.Name)
- }
- break
- }
- }
- return valid
- }
|