bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/conf/system.go (about) 1 package conf 2 3 import ( 4 "bytes" 5 "fmt" 6 "net/http" 7 "net/http/httputil" 8 "net/url" 9 "strings" 10 "time" 11 12 "bosun.org/cloudwatch" 13 "bosun.org/slog" 14 15 "bosun.org/cmd/bosun/expr" 16 "bosun.org/graphite" 17 "bosun.org/opentsdb" 18 ainsightsmgmt "github.com/Azure/azure-sdk-for-go/services/appinsights/mgmt/2015-05-01/insights" 19 ainsights "github.com/Azure/azure-sdk-for-go/services/appinsights/v1/insights" 20 "github.com/influxdata/influxdb/client/v2" 21 promapi "github.com/prometheus/client_golang/api" 22 promv1 "github.com/prometheus/client_golang/api/prometheus/v1" 23 24 "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2018-03-01/insights" 25 "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-02-01/resources" 26 "github.com/Azure/go-autorest/autorest" 27 "github.com/Azure/go-autorest/autorest/azure/auth" 28 "github.com/BurntSushi/toml" 29 ) 30 31 // SystemConf contains all the information that bosun needs to run. Outside of the conf package 32 // usage should be through conf.SystemConfProvider 33 type SystemConf struct { 34 HTTPListen string 35 HTTPSListen string 36 TLSCertFile string 37 TLSKeyFile string 38 39 Hostname string 40 Scheme string // default http 41 Ping bool 42 PingDuration Duration // Duration from now to stop pinging hosts based on time since the host tag was touched 43 TimeAndDate []int // timeanddate.com cities list 44 SearchSince Duration 45 ShortURLKey string 46 InternetProxy string 47 MinGroupSize int 48 49 UnknownThreshold int 50 CheckFrequency Duration // Time between alert checks: 5m 51 DefaultRunEvery int // Default number of check intervals to run each alert: 1 52 AlertCheckDistribution string // Method to distribute alet checks. No distribution if equals "" 53 54 DBConf DBConf 55 56 SMTPConf SMTPConf 57 58 RuleVars map[string]string 59 60 ExampleExpression string 61 62 OpenTSDBConf OpenTSDBConf 63 GraphiteConf GraphiteConf 64 InfluxConf InfluxConf 65 ElasticConf map[string]ElasticConf 66 AzureMonitorConf map[string]AzureMonitorConf 67 PromConf map[string]PromConf 68 CloudWatchConf CloudWatchConf 69 AnnotateConf AnnotateConf 70 71 AuthConf *AuthConf 72 73 MaxRenderedTemplateAge int // in days 74 75 EnableSave bool 76 EnableReload bool 77 CommandHookPath string 78 RuleFilePath string 79 md toml.MetaData 80 } 81 82 // EnabledBackends stores which query backends supported by bosun are enabled 83 // via the system configuration. This is used so it can be passed to the rule parser 84 // and the parse errors can be thrown for query functions that are used when the backend 85 // is not enabled 86 type EnabledBackends struct { 87 OpenTSDB bool 88 Graphite bool 89 Influx bool 90 Elastic bool 91 Annotate bool 92 AzureMonitor bool 93 CloudWatch bool 94 Prom bool 95 } 96 97 // EnabledBackends returns and EnabledBackends struct which contains fields 98 // to state if a backend is enabled in the configuration or not 99 func (sc *SystemConf) EnabledBackends() EnabledBackends { 100 b := EnabledBackends{} 101 b.OpenTSDB = sc.OpenTSDBConf.Host != "" 102 b.Graphite = sc.GraphiteConf.Host != "" 103 b.Influx = sc.InfluxConf.URL != "" 104 b.Prom = sc.PromConf["default"].URL != "" 105 b.Elastic = len(sc.ElasticConf["default"].Hosts) != 0 106 b.Annotate = len(sc.AnnotateConf.Hosts) != 0 107 b.AzureMonitor = len(sc.AzureMonitorConf) != 0 108 b.CloudWatch = sc.CloudWatchConf.Enabled 109 return b 110 } 111 112 // OpenTSDBConf contains OpenTSDB specific configuration information. The ResponseLimit 113 // will prevent Bosun from loading responses larger than its size in bytes. The version 114 // enables certain features of OpenTSDB querying 115 type OpenTSDBConf struct { 116 ResponseLimit int64 117 Host string // OpenTSDB relay and query destination: ny-devtsdb04:4242 118 Version opentsdb.Version // If set to 2.2 , enable passthrough of wildcards and filters, and add support for groupby 119 } 120 121 // GraphiteConf contains a string representing the host of a graphite server and 122 // a map of headers to be sent with each Graphite request 123 type GraphiteConf struct { 124 Host string 125 Headers map[string]string 126 } 127 128 // AnnotateConf contains the elastic configuration to enable Annotations support 129 type AnnotateConf struct { 130 Hosts []string // CSV of Elastic Hosts, currently the only backend in annotate 131 Version string 132 SimpleClient bool // If true ES will connect over NewSimpleClient 133 ClientOptions ESClientOptions // ES client options 134 Index string // name of index / table 135 } 136 137 // ESClientOptions: elastic search client options 138 // reference https://github.com/olivere/elastic/blob/release-branch.v3/client.go#L107 139 type ESClientOptions struct { 140 Enabled bool // if true use client option else ignore 141 BasicAuthUsername string // username for HTTP Basic Auth 142 BasicAuthPassword string // password for HTTP Basic Auth 143 Scheme string // https (default http) 144 SnifferEnabled bool // sniffer enabled or disabled 145 SnifferTimeoutStartup time.Duration // in seconds (default is 5 sec) 146 SnifferTimeout time.Duration // in seconds (default is 2 sec) 147 SnifferInterval time.Duration // in minutes (default is 15 min) 148 HealthcheckEnabled bool // healthchecks enabled or disabled 149 HealthcheckTimeoutStartup time.Duration // in seconds (default is 5 sec) 150 HealthcheckTimeout time.Duration // in seconds (default is 1 sec) 151 HealthcheckInterval time.Duration // in seconds (default is 60 sec) 152 MaxRetries int // max. number of retries before giving up (default 10) 153 GzipEnabled bool // enables or disables gzip compression (disabled by default) 154 155 } 156 157 // ElasticConf contains configuration for an elastic host that Bosun can query 158 type ElasticConf AnnotateConf 159 160 // AzureConf contains configuration for an Azure metrics 161 type AzureMonitorConf struct { 162 SubscriptionId string 163 TenantId string 164 ClientId string 165 ClientSecret string 166 Concurrency int 167 DebugRequest bool 168 DebugResponse bool 169 } 170 171 // Valid returns if the configuration for the AzureMonitor has 172 // required fields with appropriate values 173 func (ac AzureMonitorConf) Valid() error { 174 present := make(map[string]bool) 175 missing := []string{} 176 errors := []string{} 177 present["SubscriptionId"] = ac.SubscriptionId != "" 178 present["TenantId"] = ac.TenantId != "" 179 present["ClientId"] = ac.ClientId != "" 180 present["ClientSecret"] = ac.ClientSecret != "" 181 for k, v := range present { 182 if !v { 183 missing = append(missing, k) 184 } 185 } 186 if len(missing) != 0 { 187 errors = append(errors, fmt.Sprintf("missing required fields: %v", strings.Join(missing, ", "))) 188 } else { 189 ccc := auth.NewClientCredentialsConfig(ac.ClientId, ac.ClientSecret, ac.TenantId) 190 _, err := ccc.Authorizer() // We don't use the value here, only checking for error 191 if err != nil { 192 errors = append(errors, fmt.Sprintf("problem creating valid authorization: %v", err.Error())) 193 } 194 } 195 if ac.Concurrency < 0 { 196 errors = append(errors, fmt.Sprintf("concurrency is %v and must be 0 or greater", ac.Concurrency)) 197 } 198 if len(errors) != 0 { 199 return fmt.Errorf("%v", strings.Join(errors, " and ")) 200 } 201 return nil 202 } 203 204 // InfluxConf contains configuration for an influx host that Bosun can query 205 type InfluxConf struct { 206 URL string 207 Username string 208 Password string `json:"-"` 209 UserAgent string 210 Timeout Duration 211 UnsafeSSL bool 212 Precision string 213 } 214 215 // PromConf contains configuration for a Prometheus TSDB that Bosun can query 216 type PromConf struct { 217 URL string 218 } 219 220 // Valid returns if the configuration for the PromConf has required fields needed 221 // to create a prometheus tsdb client 222 func (pc PromConf) Valid() error { 223 if pc.URL == "" { 224 return fmt.Errorf("missing URL field") 225 } 226 // NewClient makes sure the url is valid, no connections are made in this call 227 _, err := promapi.NewClient(promapi.Config{Address: pc.URL}) 228 if err != nil { 229 return err 230 } 231 return nil 232 } 233 234 // DBConf stores the connection information for Bosun's internal storage 235 type DBConf struct { 236 RedisHost string 237 RedisDb int 238 RedisPassword string 239 RedisClientSetName bool 240 RedisSentinels []string 241 RedisMasterName string 242 243 LedisDir string 244 LedisBindAddr string 245 } 246 247 // SMTPConf contains information for the mail server for which bosun will 248 // send emails through 249 type SMTPConf struct { 250 EmailFrom string 251 Host string 252 Username string 253 Password string `json:"-"` 254 } 255 256 //AuthConf is configuration for bosun's authentication 257 type AuthConf struct { 258 AuthDisabled bool 259 //Secret string to hash auth tokens. Needed to enable token auth. 260 TokenSecret string 261 //Secret sting used to encrypt cookie. 262 CookieSecret string 263 //LDAP configuration 264 LDAP LDAPConf 265 } 266 267 type LDAPConf struct { 268 // Domain name (used to make domain/username) 269 Domain string 270 //user base dn (LDAP Auth) 271 UserBaseDn string 272 // LDAP server 273 LdapAddr string 274 // allow insecure ldap connection? 275 AllowInsecure bool 276 // default permission level for anyone who can log in. Try "Reader". 277 DefaultPermission string 278 //List of group level permissions 279 Groups []LDAPGroup 280 //List of user specific permission levels 281 Users map[string]string 282 //Root search path for group lookups. Usually something like "DC=myorg,DC=com". 283 //Only needed if using group permissions 284 RootSearchPath string 285 } 286 287 //LDAPGroup is a Group level access specification for ldap 288 type LDAPGroup struct { 289 // group search path string 290 Path string 291 // Access to grant members of group Ex: "Admin" 292 Role string 293 } 294 295 type CloudWatchConf struct { 296 Enabled bool 297 ExpansionLimit int 298 PagesLimit int 299 Concurrency int 300 } 301 302 func (c CloudWatchConf) Valid() error { 303 // Check Cloudwatch Configuration 304 if c.PagesLimit < 1 { 305 return fmt.Errorf(`error in cloudwatch configuration. PagesLimit must be greater than 0`) 306 } 307 308 if c.ExpansionLimit < 1 { 309 return fmt.Errorf(`error in cloudwatch configuration. ExpansionLimit must be greater than 0`) 310 } 311 return nil 312 } 313 314 // GetSystemConfProvider returns the SystemConfProvider interface 315 // and validates the logic of the configuration. If the configuration 316 // is not valid an error is returned 317 func (sc *SystemConf) GetSystemConfProvider() (SystemConfProvider, error) { 318 var provider SystemConfProvider = sc 319 if err := ValidateSystemConf(sc); err != nil { 320 return provider, err 321 } 322 return provider, nil 323 } 324 325 const ( 326 defaultHTTPListen = ":8070" 327 ) 328 329 // NewSystemConf retruns a system conf with default values set 330 func newSystemConf() *SystemConf { 331 return &SystemConf{ 332 Scheme: "http", 333 CheckFrequency: Duration{Duration: time.Minute * 5}, 334 DefaultRunEvery: 1, 335 HTTPListen: defaultHTTPListen, 336 AlertCheckDistribution: "", 337 DBConf: DBConf{ 338 LedisDir: "ledis_data", 339 LedisBindAddr: "127.0.0.1:9565", 340 RedisClientSetName: true, 341 }, 342 MinGroupSize: 5, 343 PingDuration: Duration{Duration: time.Hour * 24}, 344 OpenTSDBConf: OpenTSDBConf{ 345 ResponseLimit: 1 << 20, // 1MB 346 Version: opentsdb.Version2_1, 347 }, 348 SearchSince: Duration{time.Duration(opentsdb.Day) * 3}, 349 UnknownThreshold: 5, 350 } 351 } 352 353 // LoadSystemConfigFile loads the system configuration in TOML format. It will 354 // error if there are values in the config that were not parsed 355 func LoadSystemConfigFile(fileName string) (*SystemConf, error) { 356 return loadSystemConfig(fileName, true) 357 } 358 359 // LoadSystemConfig is like LoadSystemConfigFile but loads the config from a string 360 func LoadSystemConfig(conf string) (*SystemConf, error) { 361 return loadSystemConfig(conf, false) 362 } 363 364 func loadSystemConfig(conf string, isFileName bool) (*SystemConf, error) { 365 sc := newSystemConf() 366 var decodeMeta toml.MetaData 367 var err error 368 if isFileName { 369 decodeMeta, err = toml.DecodeFile(conf, &sc) 370 } else { 371 decodeMeta, err = toml.Decode(conf, &sc) 372 } 373 if err != nil { 374 return sc, err 375 } 376 if len(decodeMeta.Undecoded()) > 0 { 377 return sc, fmt.Errorf("undecoded fields in system configuration: %v", decodeMeta.Undecoded()) 378 } 379 380 if sc.GetAlertCheckDistribution() != "" && sc.GetAlertCheckDistribution() != "simple" { 381 return sc, fmt.Errorf("invalid value %v for AlertCheckDistribution", sc.GetAlertCheckDistribution()) 382 } 383 384 // iterate over each hosts 385 for hostPrefix, value := range sc.ElasticConf { 386 if value.SimpleClient && value.ClientOptions.Enabled { 387 return sc, fmt.Errorf("Can't use both ES SimpleClient and ES ClientOptions please remove or disable one in ElasticConf.%s: %#v", hostPrefix, sc.ElasticConf) 388 } 389 } 390 391 if sc.AnnotateConf.SimpleClient && sc.AnnotateConf.ClientOptions.Enabled { 392 return sc, fmt.Errorf("Can't use both ES SimpleClient and ES ClientOptions please remove or disable one in AnnotateConf: %#v", sc.AnnotateConf) 393 } 394 395 // Check Azure Monitor Configurations 396 for prefix, conf := range sc.AzureMonitorConf { 397 if err := conf.Valid(); err != nil { 398 return sc, fmt.Errorf(`error in configuration for Azure client "%v": %v`, prefix, err) 399 } 400 } 401 402 // Check Prometheus Monitor Configurations 403 for prefix, conf := range sc.PromConf { 404 if err := conf.Valid(); err != nil { 405 return sc, fmt.Errorf(`error in configuration for Prometheus client "%v": %v`, prefix, err) 406 } 407 } 408 409 sc.md = decodeMeta 410 // clear default http listen if not explicitly specified 411 if !decodeMeta.IsDefined("HTTPListen") && decodeMeta.IsDefined("HTTPSListen") { 412 sc.HTTPListen = "" 413 } 414 return sc, nil 415 } 416 417 // GetHTTPListen returns the hostname:port that Bosun should listen on 418 func (sc *SystemConf) GetHTTPListen() string { 419 return sc.HTTPListen 420 } 421 422 // GetHTTPSListen returns the hostname:port that Bosun should listen on with tls 423 func (sc *SystemConf) GetHTTPSListen() string { 424 return sc.HTTPSListen 425 } 426 427 // GetTLSCertFile returns the path to the tls certificate to listen with (pem format). Must be specified with HTTPSListen. 428 func (sc *SystemConf) GetTLSCertFile() string { 429 return sc.TLSCertFile 430 } 431 432 // GetTLSKeyFile returns the path to the tls key to listen with (pem format). Must be specified with HTTPSListen. 433 func (sc *SystemConf) GetTLSKeyFile() string { 434 return sc.TLSKeyFile 435 } 436 437 // GetSMTPHost returns the SMTP mail server host that Bosun will use to relay through 438 func (sc *SystemConf) GetSMTPHost() string { 439 return sc.SMTPConf.Host 440 } 441 442 // GetSMTPUsername returns the SMTP username that Bosun will use to connect to the mail server 443 func (sc *SystemConf) GetSMTPUsername() string { 444 return sc.SMTPConf.Username 445 } 446 447 // GetSMTPPassword returns the SMTP password that Bosun will use to connect to the mail server 448 func (sc *SystemConf) GetSMTPPassword() string { 449 return sc.SMTPConf.Password 450 } 451 452 // GetEmailFrom returns the email address that Bosun will use to send mail notifications from 453 func (sc *SystemConf) GetEmailFrom() string { 454 return sc.SMTPConf.EmailFrom 455 } 456 457 // GetPing returns if Bosun's pinging is enabled. When Ping is enabled, bosun will ping all hosts 458 // that is has indexed and record metrics about those pings. 459 func (sc *SystemConf) GetPing() bool { 460 return sc.Ping 461 } 462 463 // GetPingDuration returns the duration that discovered hosts (will be pinged until 464 // the host is not seen. 465 func (sc *SystemConf) GetPingDuration() time.Duration { 466 return sc.PingDuration.Duration 467 } 468 469 // GetLedisDir returns the directory where Ledis should store its files 470 func (sc *SystemConf) GetLedisDir() string { 471 return sc.DBConf.LedisDir 472 } 473 474 // GetLedisBindAddr returns the address that Ledis should listen on 475 func (sc *SystemConf) GetLedisBindAddr() string { 476 return sc.DBConf.LedisBindAddr 477 } 478 479 // GetRedisHost returns the host to use for Redis. If this is set than Redis 480 // will be used instead of Ledis. 481 func (sc *SystemConf) GetRedisHost() []string { 482 if sc.GetRedisMasterName() != "" { 483 return sc.DBConf.RedisSentinels 484 } 485 if sc.DBConf.RedisHost != "" { 486 return []string{sc.DBConf.RedisHost} 487 } 488 return []string{} 489 } 490 491 // GetRedisMasterName returns master name of redis instance within sentinel. 492 // If this is return none empty string redis sentinel will be used 493 func (sc *SystemConf) GetRedisMasterName() string { 494 return sc.DBConf.RedisMasterName 495 } 496 497 // GetRedisDb returns the redis database number to use 498 func (sc *SystemConf) GetRedisDb() int { 499 return sc.DBConf.RedisDb 500 } 501 502 // GetRedisPassword returns the password that should be used to connect to redis 503 func (sc *SystemConf) GetRedisPassword() string { 504 return sc.DBConf.RedisPassword 505 } 506 507 // RedisClientSetName returns if CLIENT SETNAME shoud send to redis. 508 func (sc *SystemConf) IsRedisClientSetName() bool { 509 return sc.DBConf.RedisClientSetName 510 } 511 512 func (sc *SystemConf) GetAuthConf() *AuthConf { 513 return sc.AuthConf 514 } 515 516 // GetRuleVars user defined variables that will be available to the rule configuration 517 // under "$sys.". This is so values with secrets can be defined in the system configuration 518 func (sc *SystemConf) GetRuleVars() map[string]string { 519 return sc.RuleVars 520 } 521 522 // GetTimeAndDate returns the http://www.timeanddate.com/ that should be available to the UI 523 // so it can show links to translate UTC times to various timezones. This feature is only 524 // for creating UI Links as Bosun is expected to be running on a machine that is set to UTC 525 func (sc *SystemConf) GetTimeAndDate() []int { 526 return sc.TimeAndDate 527 } 528 529 // GetSearchSince returns the duration that certain search requests should filter out results 530 // if they are older (have not been indexed) since the duration 531 func (sc *SystemConf) GetSearchSince() time.Duration { 532 return sc.SearchSince.Duration 533 } 534 535 // GetCheckFrequency returns the default CheckFrequency that the schedule should run at. Checks by 536 // default will run at CheckFrequency * RunEvery 537 func (sc *SystemConf) GetCheckFrequency() time.Duration { 538 return sc.CheckFrequency.Duration 539 } 540 541 // GetDefaultRunEvery returns the default multipler of how often an alert should run based on 542 // the CheckFrequency. Checks by default will run at CheckFrequency * RunEvery 543 func (sc *SystemConf) GetDefaultRunEvery() int { 544 return sc.DefaultRunEvery 545 } 546 547 // GetAlertCheckDistribution returns if the alert rule checks are scattered over check period 548 func (sc *SystemConf) GetAlertCheckDistribution() string { 549 return sc.AlertCheckDistribution 550 } 551 552 // GetUnknownThreshold returns the threshold in which multiple unknown alerts in a check iteration 553 // should be grouped into a single notification 554 func (sc *SystemConf) GetUnknownThreshold() int { 555 return sc.UnknownThreshold 556 } 557 558 // GetMinGroupSize returns the minimum number of alerts needed to group the alerts 559 // on Bosun's dashboard 560 func (sc *SystemConf) GetMinGroupSize() int { 561 return sc.MinGroupSize 562 } 563 564 // GetShortURLKey returns the API key that should be used to generate https://goo.gl/ shortlinks 565 // from Bosun's UI 566 func (sc *SystemConf) GetShortURLKey() string { 567 return sc.ShortURLKey 568 } 569 570 // GetInternetProxy sets a proxy for outgoing network requests from Bosun. Currently it 571 // only impacts requests made for shortlinks to https://goo.gl/ 572 func (sc *SystemConf) GetInternetProxy() string { 573 return sc.InternetProxy 574 } 575 576 // GetMaxRenderedTemplateAge returns the maximum time in days to keep rendered templates 577 // after the incident end date. 578 func (sc *SystemConf) GetMaxRenderedTemplateAge() int { 579 return sc.MaxRenderedTemplateAge 580 } 581 582 // SaveEnabled returns if saving via the UI and config editing API endpoints should be enabled 583 func (sc *SystemConf) SaveEnabled() bool { 584 return sc.EnableSave 585 } 586 587 // ReloadEnabled returns if reloading of the rule config should be enabled. This will return 588 // true if save is enabled but reload is not enabled. 589 func (sc *SystemConf) ReloadEnabled() bool { 590 return sc.EnableSave || sc.EnableReload 591 } 592 593 // GetCommandHookPath returns the path of a command that should be run on every save 594 func (sc *SystemConf) GetCommandHookPath() string { 595 return sc.CommandHookPath 596 } 597 598 // GetRuleFilePath returns the path to the file containing contains rules 599 // rules include Alerts, Macros, Notifications, Templates, and Global Variables 600 func (sc *SystemConf) GetRuleFilePath() string { 601 return sc.RuleFilePath 602 } 603 604 // SetTSDBHost sets the OpenTSDB host and used when Bosun is set to readonly mode 605 func (sc *SystemConf) SetTSDBHost(tsdbHost string) { 606 sc.OpenTSDBConf.Host = tsdbHost 607 } 608 609 // GetExampleExpression returns the default expression for "Expression" tab. 610 func (sc *SystemConf) GetExampleExpression() string { 611 return sc.ExampleExpression 612 } 613 614 // GetTSDBHost returns the configured TSDBHost 615 func (sc *SystemConf) GetTSDBHost() string { 616 return sc.OpenTSDBConf.Host 617 } 618 619 // GetAnnotateElasticHosts returns the Elastic hosts that should be used for annotations. 620 // Annotations are not enabled if this has no hosts 621 func (sc *SystemConf) GetAnnotateElasticHosts() expr.ElasticConfig { 622 return parseESAnnoteConfig(sc) 623 } 624 625 // GetAnnotateIndex returns the name of the Elastic index that should be used for annotations 626 func (sc *SystemConf) GetAnnotateIndex() string { 627 return sc.AnnotateConf.Index 628 } 629 630 // GetTSDBContext returns an OpenTSDB context limited to 631 // c.ResponseLimit. A nil context is returned if TSDBHost is not set. 632 func (sc *SystemConf) GetTSDBContext() opentsdb.Context { 633 if sc.OpenTSDBConf.Host == "" { 634 return nil 635 } 636 return opentsdb.NewLimitContext(sc.OpenTSDBConf.Host, sc.OpenTSDBConf.ResponseLimit, sc.OpenTSDBConf.Version) 637 } 638 639 // GetGraphiteContext returns a Graphite context which contains all the information needed 640 // to query Graphite. A nil context is returned if GraphiteHost is not set. 641 func (sc *SystemConf) GetGraphiteContext() graphite.Context { 642 if sc.GraphiteConf.Host == "" { 643 return nil 644 } 645 if len(sc.GraphiteConf.Headers) > 0 { 646 headers := http.Header(make(map[string][]string)) 647 for k, v := range sc.GraphiteConf.Headers { 648 headers.Add(k, v) 649 } 650 return graphite.HostHeader{ 651 Host: sc.GraphiteConf.Host, 652 Header: headers, 653 } 654 } 655 return graphite.Host(sc.GraphiteConf.Host) 656 } 657 658 // GetInfluxContext returns a Influx context which contains all the information needed 659 // to query Influx. 660 func (sc *SystemConf) GetInfluxContext() client.HTTPConfig { 661 c := client.HTTPConfig{} 662 if sc.md.IsDefined("InfluxConf", "URL") { 663 c.Addr = sc.InfluxConf.URL 664 } 665 if sc.md.IsDefined("InfluxConf", "Username") { 666 c.Username = sc.InfluxConf.Username 667 } 668 if sc.md.IsDefined("InfluxConf", "Password") { 669 c.Password = sc.InfluxConf.Password 670 } 671 if sc.md.IsDefined("InfluxConf", "UserAgent") { 672 c.UserAgent = sc.InfluxConf.UserAgent 673 } 674 if sc.md.IsDefined("InfluxConf", "Timeout") { 675 c.Timeout = sc.InfluxConf.Timeout.Duration 676 } 677 if sc.md.IsDefined("InfluxConf", "UnsafeSsl") { 678 c.InsecureSkipVerify = sc.InfluxConf.UnsafeSSL 679 } 680 return c 681 } 682 683 func (sc *SystemConf) GetCloudWatchContext() cloudwatch.Context { 684 c := cloudwatch.GetContext() 685 return c 686 } 687 688 // GetPromContext initializes returns a collection of Prometheus API v1 client APIs (connections) 689 // from the configuration 690 func (sc *SystemConf) GetPromContext() expr.PromClients { 691 clients := make(expr.PromClients) 692 for prefix, conf := range sc.PromConf { 693 // Error is checked in validation (PromConf Valid()) 694 client, _ := promapi.NewClient(promapi.Config{Address: conf.URL}) 695 clients[prefix] = promv1.NewAPI(client) 696 } 697 return clients 698 } 699 700 // GetElasticContext returns an Elastic context which contains all the information 701 // needed to run Elastic queries. 702 func (sc *SystemConf) GetElasticContext() expr.ElasticHosts { 703 return parseESConfig(sc) 704 } 705 706 // GetAzureMonitorContext returns a the collection of API clients needed 707 // query the Azure Monitor and Application Insights APIs 708 func (sc *SystemConf) GetAzureMonitorContext() expr.AzureMonitorClients { 709 allClients := make(expr.AzureMonitorClients) 710 for prefix, conf := range sc.AzureMonitorConf { 711 cc := expr.AzureMonitorClientCollection{} 712 cc.TenantId = conf.TenantId 713 if conf.Concurrency == 0 { 714 cc.Concurrency = 10 715 } else { 716 cc.Concurrency = conf.Concurrency 717 } 718 cc.MetricsClient = insights.NewMetricsClient(conf.SubscriptionId) 719 cc.MetricDefinitionsClient = insights.NewMetricDefinitionsClient(conf.SubscriptionId) 720 cc.ResourcesClient = resources.NewClient(conf.SubscriptionId) 721 cc.AIComponentsClient = ainsightsmgmt.NewComponentsClient(conf.SubscriptionId) 722 cc.AIMetricsClient = ainsights.NewMetricsClient() 723 if conf.DebugRequest { 724 cc.ResourcesClient.RequestInspector, cc.MetricsClient.RequestInspector, cc.MetricDefinitionsClient.RequestInspector = azureLogRequest(), azureLogRequest(), azureLogRequest() 725 cc.AIComponentsClient.RequestInspector, cc.AIMetricsClient.RequestInspector = azureLogRequest(), azureLogRequest() 726 } 727 if conf.DebugResponse { 728 cc.ResourcesClient.ResponseInspector, cc.MetricsClient.ResponseInspector, cc.MetricDefinitionsClient.ResponseInspector = azureLogResponse(), azureLogResponse(), azureLogResponse() 729 cc.AIComponentsClient.ResponseInspector, cc.AIMetricsClient.ResponseInspector = azureLogResponse(), azureLogResponse() 730 } 731 ccc := auth.NewClientCredentialsConfig(conf.ClientId, conf.ClientSecret, conf.TenantId) 732 at, err := ccc.Authorizer() 733 if err != nil { 734 // Should not hit this since we check for authorizer errors in Validation 735 // This is checked before because this method is not called until the an expression is called 736 slog.Error("unexpected Azure Authorizer error: ", err) 737 } 738 // Application Insights needs a different authorizer to use the other Resource "api.application..." 739 rcc := auth.NewClientCredentialsConfig(conf.ClientId, conf.ClientSecret, conf.TenantId) 740 rcc.Resource = "https://api.applicationinsights.io" 741 rat, err := rcc.Authorizer() 742 if err != nil { 743 slog.Error("unexpected application insights azure authorizer error: ", err) 744 } 745 cc.MetricsClient.Authorizer, cc.MetricDefinitionsClient.Authorizer, cc.ResourcesClient.Authorizer = at, at, at 746 cc.AIComponentsClient.Authorizer, cc.AIMetricsClient.Authorizer = at, rat 747 allClients[prefix] = cc 748 } 749 return allClients 750 } 751 752 // azureLogRequest outputs HTTP requests to Azure to the logs 753 func azureLogRequest() autorest.PrepareDecorator { 754 return func(p autorest.Preparer) autorest.Preparer { 755 return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { 756 r, err := p.Prepare(r) 757 if err != nil { 758 slog.Warningf("failure to dump azure request: %v", err) 759 } 760 dump, err := httputil.DumpRequestOut(r, true) 761 if err != nil { 762 slog.Warningf("failure to dump azure request: %v", err) 763 } 764 slog.Info(string(dump)) 765 return r, err 766 }) 767 } 768 } 769 770 // azureLogRequest outputs HTTP responses from requests to Azure to the logs 771 func azureLogResponse() autorest.RespondDecorator { 772 return func(p autorest.Responder) autorest.Responder { 773 return autorest.ResponderFunc(func(r *http.Response) error { 774 err := p.Respond(r) 775 if err != nil { 776 slog.Warningf("failure to dump azure response: %v", err) 777 } 778 dump, err := httputil.DumpResponse(r, true) 779 if err != nil { 780 slog.Warningf("failure to dump azure response: %v", err) 781 } 782 slog.Info(string(dump)) 783 return err 784 }) 785 } 786 } 787 788 // AnnotateEnabled returns if annotations have been enabled or not 789 func (sc *SystemConf) AnnotateEnabled() bool { 790 return len(sc.AnnotateConf.Hosts) != 0 791 } 792 793 // MakeLink creates a HTML Link based on Bosun's configured Hostname 794 func (sc *SystemConf) MakeLink(path string, v *url.Values) string { 795 u := url.URL{ 796 Scheme: sc.Scheme, 797 Host: sc.Hostname, 798 Path: path, 799 } 800 if v != nil { 801 u.RawQuery = v.Encode() 802 } 803 return u.String() 804 } 805 806 // Duration is a time.Duration with a UnmarshalText method so 807 // durations can be decoded from TOML. 808 type Duration struct { 809 time.Duration 810 } 811 812 // UnmarshalText is the method called by TOML when decoding a value 813 func (d *Duration) UnmarshalText(text []byte) error { 814 var err error 815 d.Duration, err = time.ParseDuration(string(text)) 816 return err 817 } 818 819 // URL is a *url.URL with a UnmarshalText method so 820 // a url can be decoded from TOML. 821 type URL struct { 822 *url.URL 823 } 824 825 // UnmarshalText is the method called by TOML when decoding a value 826 func (u *URL) UnmarshalText(text []byte) error { 827 var err error 828 u.URL, err = url.Parse(string(bytes.Trim(text, `\"`))) 829 return err 830 }