1package conf 2 3import ( 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 33type 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 86type 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 99func (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 115type 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 123type GraphiteConf struct { 124 Host string 125 Headers map[string]string 126} 127 128// AnnotateConf contains the elastic configuration to enable Annotations support 129type 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 139type 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 158type ElasticConf AnnotateConf 159 160// AzureConf contains configuration for an Azure metrics 161type 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 173func (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 205type 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 216type 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 222func (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 235type 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 249type 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 257type 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 267type 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 288type LDAPGroup struct { 289 // group search path string 290 Path string 291 // Access to grant members of group Ex: "Admin" 292 Role string 293} 294 295type CloudWatchConf struct { 296 Enabled bool 297 ExpansionLimit int 298 PagesLimit int 299 Concurrency int 300} 301 302func (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 317func (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 325const ( 326 defaultHTTPListen = ":8070" 327) 328 329// NewSystemConf retruns a system conf with default values set 330func 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 355func LoadSystemConfigFile(fileName string) (*SystemConf, error) { 356 return loadSystemConfig(fileName, true) 357} 358 359// LoadSystemConfig is like LoadSystemConfigFile but loads the config from a string 360func LoadSystemConfig(conf string) (*SystemConf, error) { 361 return loadSystemConfig(conf, false) 362} 363 364func 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 418func (sc *SystemConf) GetHTTPListen() string { 419 return sc.HTTPListen 420} 421 422// GetHTTPSListen returns the hostname:port that Bosun should listen on with tls 423func (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. 428func (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. 433func (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 438func (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 443func (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 448func (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 453func (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. 459func (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. 465func (sc *SystemConf) GetPingDuration() time.Duration { 466 return sc.PingDuration.Duration 467} 468 469// GetLedisDir returns the directory where Ledis should store its files 470func (sc *SystemConf) GetLedisDir() string { 471 return sc.DBConf.LedisDir 472} 473 474// GetLedisBindAddr returns the address that Ledis should listen on 475func (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. 481func (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 493func (sc *SystemConf) GetRedisMasterName() string { 494 return sc.DBConf.RedisMasterName 495} 496 497// GetRedisDb returns the redis database number to use 498func (sc *SystemConf) GetRedisDb() int { 499 return sc.DBConf.RedisDb 500} 501 502// GetRedisPassword returns the password that should be used to connect to redis 503func (sc *SystemConf) GetRedisPassword() string { 504 return sc.DBConf.RedisPassword 505} 506 507// RedisClientSetName returns if CLIENT SETNAME shoud send to redis. 508func (sc *SystemConf) IsRedisClientSetName() bool { 509 return sc.DBConf.RedisClientSetName 510} 511 512func (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 518func (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 525func (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 531func (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 537func (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 543func (sc *SystemConf) GetDefaultRunEvery() int { 544 return sc.DefaultRunEvery 545} 546 547// GetAlertCheckDistribution returns if the alert rule checks are scattered over check period 548func (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 554func (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 560func (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 566func (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/ 572func (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. 578func (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 583func (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. 589func (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 594func (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 600func (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 605func (sc *SystemConf) SetTSDBHost(tsdbHost string) { 606 sc.OpenTSDBConf.Host = tsdbHost 607} 608 609// GetExampleExpression returns the default expression for "Expression" tab. 610func (sc *SystemConf) GetExampleExpression() string { 611 return sc.ExampleExpression 612} 613 614// GetTSDBHost returns the configured TSDBHost 615func (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 621func (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 626func (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. 632func (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. 641func (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. 660func (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 683func (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 690func (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. 702func (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 708func (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 753func 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 771func 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 789func (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 794func (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. 808type Duration struct { 809 time.Duration 810} 811 812// UnmarshalText is the method called by TOML when decoding a value 813func (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. 821type URL struct { 822 *url.URL 823} 824 825// UnmarshalText is the method called by TOML when decoding a value 826func (u *URL) UnmarshalText(text []byte) error { 827 var err error 828 u.URL, err = url.Parse(string(bytes.Trim(text, `\"`))) 829 return err 830} 831