208 lines
5.1 KiB
Go
208 lines
5.1 KiB
Go
|
package checkmgr
|
||
|
|
||
|
import (
|
||
|
"crypto/rand"
|
||
|
"crypto/sha256"
|
||
|
"encoding/hex"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/circonus-labs/circonus-gometrics/api"
|
||
|
)
|
||
|
|
||
|
// Initialize CirconusMetrics instance. Attempt to find a check otherwise create one.
|
||
|
// use cases:
|
||
|
//
|
||
|
// check [bundle] by submission url
|
||
|
// check [bundle] by *check* id (note, not check_bundle id)
|
||
|
// check [bundle] by search
|
||
|
// create check [bundle]
|
||
|
func (cm *CheckManager) initializeTrapURL() error {
|
||
|
if cm.trapURL != "" {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
cm.trapmu.Lock()
|
||
|
defer cm.trapmu.Unlock()
|
||
|
|
||
|
if cm.checkSubmissionURL != "" {
|
||
|
if !cm.enabled {
|
||
|
cm.trapURL = cm.checkSubmissionURL
|
||
|
cm.trapLastUpdate = time.Now()
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !cm.enabled {
|
||
|
return errors.New("Unable to initialize trap, check manager is disabled.")
|
||
|
}
|
||
|
|
||
|
var err error
|
||
|
var check *api.Check
|
||
|
var checkBundle *api.CheckBundle
|
||
|
var broker *api.Broker
|
||
|
|
||
|
if cm.checkSubmissionURL != "" {
|
||
|
check, err = cm.apih.FetchCheckBySubmissionURL(cm.checkSubmissionURL)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
// extract check id from check object returned from looking up using submission url
|
||
|
// set m.CheckId to the id
|
||
|
// set m.SubmissionUrl to "" to prevent trying to search on it going forward
|
||
|
// use case: if the broker is changed in the UI metrics would stop flowing
|
||
|
// unless the new submission url can be fetched with the API (which is no
|
||
|
// longer possible using the original submission url)
|
||
|
var id int
|
||
|
id, err = strconv.Atoi(strings.Replace(check.Cid, "/check/", "", -1))
|
||
|
if err == nil {
|
||
|
cm.checkID = api.IDType(id)
|
||
|
cm.checkSubmissionURL = ""
|
||
|
} else {
|
||
|
cm.Log.Printf(
|
||
|
"[WARN] SubmissionUrl check to Check ID: unable to convert %s to int %q\n",
|
||
|
check.Cid, err)
|
||
|
}
|
||
|
} else if cm.checkID > 0 {
|
||
|
check, err = cm.apih.FetchCheckByID(cm.checkID)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
} else {
|
||
|
searchCriteria := fmt.Sprintf(
|
||
|
"(active:1)(host:\"%s\")(type:\"%s\")(tags:%s)",
|
||
|
cm.checkInstanceID, cm.checkType, cm.checkSearchTag)
|
||
|
checkBundle, err = cm.checkBundleSearch(searchCriteria)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if checkBundle == nil {
|
||
|
// err==nil && checkBundle==nil is "no check bundles matched"
|
||
|
// an error *should* be returned for any other invalid scenario
|
||
|
checkBundle, broker, err = cm.createNewCheck()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if checkBundle == nil {
|
||
|
if check != nil {
|
||
|
checkBundle, err = cm.apih.FetchCheckBundleByCID(api.CIDType(check.CheckBundleCid))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
} else {
|
||
|
return fmt.Errorf("[ERROR] Unable to retrieve, find, or create check")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if broker == nil {
|
||
|
broker, err = cm.apih.FetchBrokerByCID(api.CIDType(checkBundle.Brokers[0]))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// retain to facilitate metric management (adding new metrics specifically)
|
||
|
cm.checkBundle = checkBundle
|
||
|
cm.inventoryMetrics()
|
||
|
|
||
|
// url to which metrics should be PUT
|
||
|
cm.trapURL = api.URLType(checkBundle.Config.SubmissionURL)
|
||
|
|
||
|
// used when sending as "ServerName" get around certs not having IP SANS
|
||
|
// (cert created with server name as CN but IP used in trap url)
|
||
|
cn, err := cm.getBrokerCN(broker, cm.trapURL)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
cm.trapCN = BrokerCNType(cn)
|
||
|
|
||
|
cm.trapLastUpdate = time.Now()
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Search for a check bundle given a predetermined set of criteria
|
||
|
func (cm *CheckManager) checkBundleSearch(criteria string) (*api.CheckBundle, error) {
|
||
|
checkBundles, err := cm.apih.CheckBundleSearch(api.SearchQueryType(criteria))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if len(checkBundles) == 0 {
|
||
|
return nil, nil // trigger creation of a new check
|
||
|
}
|
||
|
|
||
|
numActive := 0
|
||
|
checkID := -1
|
||
|
|
||
|
for idx, check := range checkBundles {
|
||
|
if check.Status == statusActive {
|
||
|
numActive++
|
||
|
checkID = idx
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if numActive > 1 {
|
||
|
return nil, fmt.Errorf("[ERROR] Multiple possibilities multiple check bundles match criteria %s\n", criteria)
|
||
|
}
|
||
|
|
||
|
return &checkBundles[checkID], nil
|
||
|
}
|
||
|
|
||
|
// Create a new check to receive metrics
|
||
|
func (cm *CheckManager) createNewCheck() (*api.CheckBundle, *api.Broker, error) {
|
||
|
checkSecret := string(cm.checkSecret)
|
||
|
if checkSecret == "" {
|
||
|
secret, err := cm.makeSecret()
|
||
|
if err != nil {
|
||
|
secret = "myS3cr3t"
|
||
|
}
|
||
|
checkSecret = secret
|
||
|
}
|
||
|
|
||
|
broker, err := cm.getBroker()
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
|
||
|
config := api.CheckBundle{
|
||
|
Brokers: []string{broker.Cid},
|
||
|
Config: api.CheckBundleConfig{AsyncMetrics: true, Secret: checkSecret},
|
||
|
DisplayName: string(cm.checkDisplayName),
|
||
|
Metrics: []api.CheckBundleMetric{},
|
||
|
MetricLimit: 0,
|
||
|
Notes: "",
|
||
|
Period: 60,
|
||
|
Status: statusActive,
|
||
|
Tags: append([]string{string(cm.checkSearchTag)}, cm.checkTags...),
|
||
|
Target: string(cm.checkInstanceID),
|
||
|
Timeout: 10,
|
||
|
Type: string(cm.checkType),
|
||
|
}
|
||
|
|
||
|
checkBundle, err := cm.apih.CreateCheckBundle(config)
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
|
||
|
return checkBundle, broker, nil
|
||
|
}
|
||
|
|
||
|
// Create a dynamic secret to use with a new check
|
||
|
func (cm *CheckManager) makeSecret() (string, error) {
|
||
|
hash := sha256.New()
|
||
|
x := make([]byte, 2048)
|
||
|
if _, err := rand.Read(x); err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
hash.Write(x)
|
||
|
return hex.EncodeToString(hash.Sum(nil))[0:16], nil
|
||
|
}
|