github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/config/config.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package config 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "errors" 23 "flag" 24 "fmt" 25 "os" 26 "runtime" 27 "strconv" 28 "strings" 29 "time" 30 31 "github.com/pbnjay/memory" 32 "github.com/siglens/siglens/pkg/config/common" 33 "github.com/siglens/siglens/pkg/hooks" 34 segutils "github.com/siglens/siglens/pkg/segment/utils" 35 "github.com/siglens/siglens/pkg/utils" 36 log "github.com/sirupsen/logrus" 37 "github.com/valyala/fasthttp" 38 "gopkg.in/yaml.v3" 39 ) 40 41 const MINUTES_REREAD_CONFIG = 15 42 const RunModFilePath = "data/common/runmod.cfg" 43 44 var configFileLastModified uint64 45 46 var runningConfig common.Configuration 47 var configFilePath string 48 49 var parallelism int64 50 51 func init() { 52 parallelism = int64(runtime.GOMAXPROCS(0)) 53 if parallelism <= 1 { 54 parallelism = 2 55 } 56 } 57 58 func GetTotalMemoryAvailable() uint64 { 59 var gogc uint64 60 v := os.Getenv("GOGC") 61 if v != "" { 62 n, err := strconv.Atoi(v) 63 if err != nil { 64 log.Error("Error while converting gogc to int") 65 n = 100 66 } 67 gogc = uint64(n) 68 } else { 69 gogc = 100 70 } 71 hostMemory := memory.TotalMemory() * runningConfig.MemoryThresholdPercent / 100 72 allowedMemory := hostMemory / (1 + gogc/100) 73 log.Infof("GOGC: %+v, MemThresholdPerc: %v, HostRAM: %+v MB, RamAllowedToUse: %v MB", gogc, 74 runningConfig.MemoryThresholdPercent, 75 segutils.ConvertUintBytesToMB(memory.TotalMemory()), 76 segutils.ConvertUintBytesToMB(allowedMemory)) 77 return allowedMemory 78 } 79 80 /* 81 Returns GOMAXPROCS 82 */ 83 func GetParallelism() int64 { 84 return parallelism 85 } 86 87 func GetDataDiskThresholdPercent() uint64 { 88 return runningConfig.DataDiskThresholdPercent 89 } 90 91 func GetRunningConfig() *common.Configuration { 92 return &runningConfig 93 } 94 95 func GetSSInstanceName() string { 96 return runningConfig.SSInstanceName 97 } 98 99 func GetEventTypeKeywords() *[]string { 100 return &runningConfig.EventTypeKeywords 101 } 102 103 func GetRetentionHours() int { 104 return runningConfig.RetentionHours 105 } 106 107 func IsS3Enabled() bool { 108 return runningConfig.S3.Enabled 109 } 110 111 func SetS3Enabled(flag bool) { 112 runningConfig.S3.Enabled = flag 113 } 114 115 func GetS3BucketName() string { 116 return runningConfig.S3.BucketName 117 } 118 119 func SetS3BucketName(bname string) { 120 runningConfig.S3.BucketName = bname 121 } 122 123 func GetS3Region() string { 124 return runningConfig.S3.RegionName 125 } 126 127 func SetS3Region(region string) { 128 runningConfig.S3.RegionName = region 129 } 130 131 func GetS3BucketPrefix() string { 132 return runningConfig.S3.BucketPrefix 133 } 134 135 func GetMaxSegFileSize() *uint64 { 136 return &runningConfig.MaxSegFileSize 137 } 138 139 func GetESVersion() *string { 140 return &runningConfig.ESVersion 141 } 142 143 // returns the configured ingest listen IP Addr 144 // if the node is not an ingest node, this will not be set 145 func GetIngestListenIP() string { 146 return runningConfig.IngestListenIP 147 } 148 149 // returns the configured query listen IP Addr 150 // if the node is not a query node, this will not be set 151 func GetQueryListenIP() string { 152 return runningConfig.QueryListenIP 153 } 154 155 // returns the configured ingest port 156 // if the node is not an ingest node, this will not be set 157 func GetIngestPort() uint64 { 158 return runningConfig.IngestPort 159 } 160 161 // returns the configured query port 162 // if the node is not a query node, this will not be set 163 func GetQueryPort() uint64 { 164 return runningConfig.QueryPort 165 } 166 167 func GetDataPath() string { 168 return runningConfig.DataPath 169 } 170 171 // returns if tls is enabled 172 func IsTlsEnabled() bool { 173 return runningConfig.TLS.Enabled 174 } 175 176 // returns the configured certificate path 177 func GetTLSCertificatePath() string { 178 return runningConfig.TLS.CertificatePath 179 } 180 181 // returns the configured private key path 182 func GetTLSPrivateKeyPath() string { 183 return runningConfig.TLS.PrivateKeyPath 184 } 185 186 // used by 187 func GetQueryHostname() string { 188 return runningConfig.QueryHostname 189 } 190 191 // returns SmtpHost, SmtpPort, SenderEmail and GmailAppPassword 192 func GetEmailConfig() (string, int, string, string) { 193 return runningConfig.EmailConfig.SmtpHost, runningConfig.EmailConfig.SmtpPort, runningConfig.EmailConfig.SenderEmail, runningConfig.EmailConfig.GmailAppPassword 194 } 195 196 func SetEmailConfig(smtpHost string, smtpPort int, senderEmail string, gmailAppPassword string) { 197 runningConfig.EmailConfig.SmtpHost = smtpHost 198 runningConfig.EmailConfig.SmtpPort = smtpPort 199 runningConfig.EmailConfig.SenderEmail = senderEmail 200 runningConfig.EmailConfig.GmailAppPassword = gmailAppPassword 201 } 202 203 func GetUIDomain() string { 204 hostname := GetQueryHostname() 205 if hostname == "" { 206 return "localhost" 207 } else { 208 return hostname 209 } 210 } 211 212 func GetSiglensDBConfig() (string, string, uint64, string, string, string) { 213 return runningConfig.DatabaseConfig.Provider, runningConfig.DatabaseConfig.Host, runningConfig.DatabaseConfig.Port, runningConfig.DatabaseConfig.User, runningConfig.DatabaseConfig.Password, runningConfig.DatabaseConfig.Dbname 214 } 215 216 func GetLogPrefix() string { 217 return runningConfig.Log.LogPrefix 218 } 219 220 func IsDebugMode() bool { 221 return runningConfig.Debug 222 } 223 224 func IsPQSEnabled() bool { 225 return runningConfig.PQSEnabledConverted 226 } 227 228 func IsAggregationsEnabled() bool { 229 return runningConfig.AgileAggsEnabledConverted 230 } 231 232 func SetAggregationsFlag(enabled bool) { 233 runningConfig.AgileAggsEnabledConverted = enabled 234 runningConfig.AgileAggsEnabled = strconv.FormatBool(enabled) 235 } 236 237 func IsAnalyticsEnabled() bool { 238 return runningConfig.AnalyticsEnabledConverted 239 } 240 241 func IsSafeMode() bool { 242 return runningConfig.SafeServerStart 243 } 244 245 func GetRunningConfigAsJsonStr() (string, error) { 246 buffer := new(bytes.Buffer) 247 encoder := json.NewEncoder(buffer) 248 encoder.SetIndent("", "") 249 err := encoder.Encode(&runningConfig) 250 return buffer.String(), err 251 } 252 253 func GetSegFlushIntervalSecs() int { 254 if runningConfig.SegFlushIntervalSecs > 600 { 255 log.Errorf("GetSegFlushIntervalSecs:SegFlushIntervalSecs cannot be more than 10 mins") 256 runningConfig.SegFlushIntervalSecs = 600 257 } 258 return runningConfig.SegFlushIntervalSecs 259 } 260 261 func GetTimeStampKey() string { 262 return runningConfig.TimeStampKey 263 } 264 265 func GetS3IngestQueueName() string { 266 return runningConfig.S3IngestQueueName 267 } 268 269 func GetS3IngestQueueRegion() string { 270 return runningConfig.S3IngestQueueRegion 271 } 272 func GetS3IngestBufferSize() uint64 { 273 return runningConfig.S3IngestBufferSize 274 } 275 func GetMaxParallelS3IngestBuffers() uint64 { 276 return runningConfig.MaxParallelS3IngestBuffers 277 } 278 279 // returns a map of s3 config 280 func GetS3ConfigMap() map[string]interface{} { 281 data, err := json.Marshal(runningConfig.S3) 282 if err != nil { 283 return map[string]interface{}{} 284 } 285 286 var newMap map[string]interface{} 287 err = json.Unmarshal(data, &newMap) 288 if err != nil { 289 return map[string]interface{}{} 290 } 291 return newMap 292 } 293 294 func IsIngestNode() bool { 295 retVal, err := strconv.ParseBool(runningConfig.IngestNode) 296 if err != nil { 297 log.Errorf("Error parsing ingest node: [%v] Err: [%+v]. Defaulting to true", runningConfig.IngestNode, err) 298 return true 299 } 300 return retVal 301 } 302 303 func IsQueryNode() bool { 304 retVal, err := strconv.ParseBool(runningConfig.QueryNode) 305 if err != nil { 306 log.Errorf("Error parsing query node: [%v] Err: [%+v]. Defaulting to true", runningConfig.QueryNode, err) 307 return true 308 } 309 return retVal 310 } 311 312 func SetEventTypeKeywords(val []string) { 313 runningConfig.EventTypeKeywords = val 314 } 315 316 func SetSegFlushIntervalSecs(val int) { 317 if val < 1 { 318 log.Errorf("SetSegFlushIntervalSecs : SegFlushIntervalSecs should not be less than 1s") 319 log.Infof("SetSegFlushIntervalSecs : Setting SegFlushIntervalSecs to 1 by default") 320 val = 1 321 } 322 runningConfig.SegFlushIntervalSecs = val 323 } 324 325 func SetRetention(val int) { 326 runningConfig.RetentionHours = val 327 } 328 329 func SetTimeStampKey(val string) { 330 runningConfig.TimeStampKey = val 331 } 332 333 func SetMaxSegFileSize(size uint64) { 334 runningConfig.MaxSegFileSize = size 335 } 336 337 func SetRunningConfig(dir string) { 338 runningConfig.DataPath = dir 339 } 340 341 func SetESVersion(val string) { 342 runningConfig.ESVersion = val 343 } 344 345 func SetSSInstanceName(val string) { 346 runningConfig.SSInstanceName = val 347 } 348 349 func SetDebugMode(log bool) { 350 runningConfig.Debug = log 351 } 352 353 func SetDataPath(path string) { 354 runningConfig.DataPath = path 355 } 356 357 func SetDataDiskThresholdPercent(percent uint64) { 358 runningConfig.DataDiskThresholdPercent = percent 359 } 360 361 func SetMaxParallelS3IngestBuffers(maxBuf uint64) { 362 runningConfig.MaxParallelS3IngestBuffers = maxBuf 363 } 364 func SetPQSEnabled(enabled bool) { 365 runningConfig.PQSEnabledConverted = enabled 366 runningConfig.PQSEnabled = strconv.FormatBool(enabled) 367 } 368 369 func SetQueryPort(value uint64) { 370 runningConfig.QueryPort = value 371 } 372 373 func ValidateDeployment() (common.DeploymentType, error) { 374 if IsQueryNode() && IsIngestNode() { 375 if runningConfig.S3.Enabled { 376 return common.SingleNodeS3, nil 377 } 378 return common.SingleNode, nil 379 } 380 return 0, fmt.Errorf("single node deployment must have both query and ingest in the same node") 381 } 382 383 func WriteToYamlConfig() { 384 setValues, err := yaml.Marshal(&runningConfig) 385 if err != nil { 386 log.Errorf("error converting to yaml: %v", err) 387 } 388 err = os.WriteFile(configFilePath, setValues, 0644) 389 if err != nil { 390 log.Errorf("error writing to yaml file: %v", err) 391 } 392 } 393 394 // InitConfigurationData is in charge to init the various Configuration data. 395 // It runs only once to instantiate Configuration options. 396 // If an error is encountered, the configuration was unable to be read, so siglens should properly exit to avoid startup with wrong configurations 397 func InitConfigurationData() error { 398 log.Trace("Initdatastructure.ConfigurationData | START") 399 configFilePath = ExtractCmdLineInput() // Function for validate command line INPUT 400 log.Trace("Initdatastructure.ConfigurationData | STOP") 401 config, err := ReadConfigFile(configFilePath) 402 if err != nil { 403 return err 404 } 405 runningConfig = config 406 var readConfig common.RunModConfig 407 readConfig, err = ReadRunModConfig(RunModFilePath) 408 if err != nil && !os.IsNotExist(err) { 409 log.Errorf("InitConfigurationData: Failed to read runmod config: %v, config: %+v", err, readConfig) 410 } 411 fileInfo, err := os.Stat(configFilePath) 412 if err != nil { 413 log.Errorf("refreshConfig: Cannot stat config file while re-reading, err= %v", err) 414 return err 415 } 416 configFileLastModified = uint64(fileInfo.ModTime().UTC().Unix()) 417 go refreshConfig() 418 return nil 419 } 420 421 /* 422 Use only for testing purpose, DO NOT use externally 423 */ 424 func InitializeDefaultConfig() { 425 runningConfig = GetTestConfig() 426 _ = InitDerivedConfig("test-uuid") // This is only used for testing 427 } 428 429 // To do - Currently we are assigning default value two times.. in InitializeDefaultConfig() for testing and 430 // ExtractConfigData(). Do this in one time. 431 func GetTestConfig() common.Configuration { 432 // ************************************* 433 // THIS IS ONLY USED in TESTS, MAKE SURE: 434 // 1. set the defaults ExtractConfigData 435 // 2. set the defaults in server.yaml 436 // 3. And Here. 437 // ************************************ 438 439 testConfig := common.Configuration{ 440 IngestListenIP: "0.0.0.0", 441 QueryListenIP: "0.0.0.0", 442 IngestPort: 8081, 443 QueryPort: 5122, 444 IngestUrl: "", 445 EventTypeKeywords: []string{"eventType"}, 446 QueryNode: "true", 447 IngestNode: "true", 448 SegFlushIntervalSecs: 5, 449 DataPath: "data/", 450 S3: common.S3Config{Enabled: false, BucketName: "", BucketPrefix: "", RegionName: ""}, 451 RetentionHours: 24 * 90, 452 TimeStampKey: "timestamp", 453 MaxSegFileSize: 1_073_741_824, 454 LicenseKeyPath: "./", 455 ESVersion: "", 456 Debug: false, 457 MemoryThresholdPercent: 80, 458 DataDiskThresholdPercent: 85, 459 S3IngestQueueName: "", 460 S3IngestQueueRegion: "", 461 S3IngestBufferSize: 1000, 462 MaxParallelS3IngestBuffers: 10, 463 SSInstanceName: "", 464 PQSEnabled: "false", 465 PQSEnabledConverted: false, 466 SafeServerStart: false, 467 AnalyticsEnabled: "false", 468 AnalyticsEnabledConverted: false, 469 AgileAggsEnabled: "true", 470 AgileAggsEnabledConverted: true, 471 QueryHostname: "", 472 Log: common.LogConfig{LogPrefix: "", LogFileRotationSizeMB: 100, CompressLogFile: false}, 473 TLS: common.TLSConfig{Enabled: false, CertificatePath: "", PrivateKeyPath: ""}, 474 DatabaseConfig: common.DatabaseConfig{Enabled: true, Provider: "sqlite"}, 475 EmailConfig: common.EmailConfig{SmtpHost: "smtp.gmail.com", SmtpPort: 587, SenderEmail: "doe1024john@gmail.com", GmailAppPassword: " "}, 476 } 477 478 return testConfig 479 } 480 481 func InitializeTestingConfig() { 482 InitializeDefaultConfig() 483 SetDebugMode(true) 484 SetDataPath("data/") 485 } 486 487 func ReadRunModConfig(fileName string) (common.RunModConfig, error) { 488 _, err := os.Stat(fileName) 489 if os.IsNotExist(err) { 490 log.Infof("ReadRunModConfig:Config file '%s' does not exist. Awaiting user action to create it.", fileName) 491 return common.RunModConfig{}, err 492 } else if err != nil { 493 log.Errorf("ReadRunModConfig:Error accessing config file '%s': %v", fileName, err) 494 return common.RunModConfig{}, err 495 } 496 497 jsonData, err := os.ReadFile(fileName) 498 if err != nil { 499 log.Errorf("ReadRunModConfig:Cannot read input fileName = %v, err=%v", fileName, err) 500 } 501 return ExtractReadRunModConfig(jsonData) 502 } 503 504 func ExtractReadRunModConfig(jsonData []byte) (common.RunModConfig, error) { 505 var runModConfig common.RunModConfig 506 err := json.Unmarshal(jsonData, &runModConfig) 507 if err != nil { 508 log.Errorf("ExtractReadRunModConfig:Failed to parse runmod.cfg: %v", err) 509 return runModConfig, err 510 } 511 512 SetPQSEnabled(runModConfig.PQSEnabled) 513 return runModConfig, nil 514 } 515 516 func ReadConfigFile(fileName string) (common.Configuration, error) { 517 yamlData, err := os.ReadFile(fileName) 518 if err != nil { 519 log.Errorf("Cannot read input fileName = %v, err=%v", fileName, err) 520 } 521 522 if hook := hooks.GlobalHooks.ExtractConfigHook; hook != nil { 523 return hook(yamlData) 524 } else { 525 return ExtractConfigData(yamlData) 526 } 527 } 528 529 func ExtractConfigData(yamlData []byte) (common.Configuration, error) { 530 var config common.Configuration 531 err := yaml.Unmarshal(yamlData, &config) 532 if err != nil { 533 log.Errorf("Error parsing yaml err=%v", err) 534 return config, err 535 } 536 537 if len(config.IngestListenIP) <= 0 { 538 config.IngestListenIP = "0.0.0.0" 539 } 540 if len(config.QueryListenIP) <= 0 { 541 config.QueryListenIP = "0.0.0.0" 542 } 543 544 if config.IngestPort <= 0 { 545 config.IngestPort = 8081 546 } 547 548 if config.QueryPort <= 0 { 549 config.QueryPort = 5122 550 } 551 552 if len(config.EventTypeKeywords) <= 0 { 553 config.EventTypeKeywords = []string{"eventType"} 554 } 555 if config.SegFlushIntervalSecs <= 0 { 556 config.SegFlushIntervalSecs = 5 557 } 558 if len(config.Log.LogPrefix) <= 0 { 559 config.Log.LogPrefix = "" 560 } 561 562 if len(config.QueryNode) <= 0 { 563 config.QueryNode = "true" 564 } 565 566 if len(config.IngestNode) <= 0 { 567 config.IngestNode = "true" 568 } 569 570 if len(config.PQSEnabled) <= 0 { 571 config.PQSEnabled = "false" 572 } 573 pqsEnabled, err := strconv.ParseBool(config.PQSEnabled) 574 if err != nil { 575 log.Errorf("ExtractConfigData: failed to parse PQS enabled flag. Defaulting to false. Error: %v", err) 576 pqsEnabled = false 577 config.PQSEnabled = "false" 578 } 579 config.PQSEnabledConverted = pqsEnabled 580 581 if len(config.AnalyticsEnabled) <= 0 { 582 config.AnalyticsEnabled = "true" 583 } 584 analyticsEnabled, err := strconv.ParseBool(config.AnalyticsEnabled) 585 if err != nil { 586 log.Errorf("ExtractConfigData: failed to parse analytics enabled flag. Defaulting to true. Error: %v", err) 587 analyticsEnabled = true 588 config.AnalyticsEnabled = "true" 589 } 590 config.AnalyticsEnabledConverted = analyticsEnabled 591 592 if len(config.AgileAggsEnabled) <= 0 { 593 config.AgileAggsEnabled = "true" 594 } 595 AgileAggsEnabled, err := strconv.ParseBool(config.AgileAggsEnabled) 596 if err != nil { 597 log.Errorf("ExtractConfigData: failed to parse AgileAggs enabled flag. Defaulting to true. Error: %v", err) 598 AgileAggsEnabled = true 599 config.AgileAggsEnabled = "true" 600 } 601 config.AgileAggsEnabledConverted = AgileAggsEnabled 602 603 if len(config.DataPath) <= 0 { 604 config.DataPath = "data/" 605 } 606 607 if len(config.IngestUrl) <= 0 { 608 config.IngestUrl = "http://localhost:" + strconv.FormatUint(config.IngestPort, 10) 609 } 610 611 if !config.S3.Enabled { 612 config.S3.Enabled = false 613 } 614 615 if len(config.S3.BucketName) <= 0 { 616 config.S3.BucketName = "" 617 } 618 if len(config.S3.RegionName) <= 0 { 619 config.S3.RegionName = "" 620 } 621 if len(config.S3.BucketPrefix) <= 0 { 622 config.S3.BucketPrefix = "" 623 } else { 624 if config.S3.BucketPrefix[len(config.S3.BucketPrefix)-1:] != "/" { 625 config.S3.BucketPrefix = config.S3.BucketPrefix + "/" 626 } 627 628 } 629 630 if config.RetentionHours == 0 { 631 log.Infof("Defaulting to 2160hrs (90 days) of retention...") 632 config.RetentionHours = 90 * 24 633 } 634 if len(config.TimeStampKey) <= 0 { 635 config.TimeStampKey = "timestamp" 636 } 637 if len(config.LicenseKeyPath) <= 0 { 638 config.LicenseKeyPath = "./" 639 } 640 if config.MaxSegFileSize <= 0 { 641 config.MaxSegFileSize = 1_073_741_824 642 } 643 if len(config.ESVersion) <= 0 { 644 config.ESVersion = "6.8.20" 645 } 646 if strings.HasPrefix(config.DataPath, "./") { 647 config.DataPath = strings.Trim(config.DataPath, "./") 648 } 649 if config.DataPath[len(config.DataPath)-1] != '/' { 650 config.DataPath += "data/" 651 } 652 if config.Log.LogFileRotationSizeMB == 0 { 653 config.Log.LogFileRotationSizeMB = 100 654 } 655 if config.DataDiskThresholdPercent == 0 { 656 config.DataDiskThresholdPercent = 85 657 } 658 if config.MemoryThresholdPercent == 0 { 659 config.MemoryThresholdPercent = 80 660 } 661 662 if len(config.S3IngestQueueName) <= 0 { 663 config.S3IngestQueueName = "" 664 } 665 if len(config.S3IngestQueueRegion) <= 0 { 666 config.S3IngestQueueRegion = "" 667 } 668 669 if config.MaxParallelS3IngestBuffers == 0 { 670 config.MaxParallelS3IngestBuffers = 10 671 } 672 673 if config.S3IngestBufferSize == 0 { 674 config.S3IngestBufferSize = 1000 675 } 676 677 if len(config.TLS.CertificatePath) >= 0 && strings.HasPrefix(config.TLS.CertificatePath, "./") { 678 config.TLS.CertificatePath = strings.Trim(config.TLS.CertificatePath, "./") 679 } 680 681 if len(config.TLS.PrivateKeyPath) >= 0 && strings.HasPrefix(config.TLS.PrivateKeyPath, "./") { 682 config.TLS.PrivateKeyPath = strings.Trim(config.TLS.PrivateKeyPath, "./") 683 } 684 685 return config, nil 686 } 687 688 func SetConfig(config common.Configuration) { 689 runningConfig = config 690 } 691 692 func ExtractCmdLineInput() string { 693 log.Trace("VerifyCommandLineInput | START") 694 configFile := flag.String("config", "server.yaml", "Path to config file") 695 696 flag.Parse() 697 log.Info("Extracting config from configFile: ", *configFile) 698 log.Trace("VerifyCommandLineInput | STOP") 699 return *configFile 700 } 701 702 // WebConfig configuration for fasthttp, copy from fasthttp 703 type WebConfig struct { 704 // Server name for sending in response headers. 705 // 706 // Default server name is used if left blank. 707 Name string 708 709 // The maximum number of concurrent connections the server may serve. 710 // 711 // DefaultConcurrency is used if not set. 712 Concurrency int 713 714 // Whether to disable keep-alive connections. 715 // 716 // The server will close all the incoming connections after sending 717 // the first response to client if this option is set to true. 718 // 719 // By default keep-alive connections are enabled. 720 DisableKeepalive bool 721 722 // Per-connection buffer size for requests' reading. 723 // This also limits the maximum header size. 724 // 725 // Increase this buffer if your clients send multi-KB RequestURIs 726 // and/or multi-KB headers (for example, BIG cookies). 727 // 728 // Default buffer size is used if not set. 729 ReadBufferSize int 730 731 // Per-connection buffer size for responses' writing. 732 // 733 // Default buffer size is used if not set. 734 WriteBufferSize int 735 736 // ReadTimeout is the amount of time allowed to read 737 // the full request including body. The connection's read 738 // deadline is reset when the connection opens, or for 739 // keep-alive connections after the first byte has been read. 740 // 741 // By default request read timeout is unlimited. 742 ReadTimeout time.Duration 743 744 // WriteTimeout is the maximum duration before timing out 745 // writes of the response. It is reset after the request handler 746 // has returned. 747 // 748 // By default response write timeout is unlimited. 749 WriteTimeout time.Duration 750 751 // IdleTimeout is the maximum amount of time to wait for the 752 // next request when keep-alive is enabled. If IdleTimeout 753 // is zero, the value of ReadTimeout is used. 754 IdleTimeout time.Duration 755 756 // Maximum number of concurrent client connections allowed per IP. 757 // 758 // By default unlimited number of concurrent connections 759 // may be established to the server from a single IP address. 760 MaxConnsPerIP int 761 762 // Maximum number of requests served per connection. 763 // 764 // The server closes connection after the last request. 765 // 'Connection: close' header is added to the last response. 766 // 767 // By default unlimited number of requests may be served per connection. 768 MaxRequestsPerConn int 769 770 // MaxKeepaliveDuration is a no-op and only left here for backwards compatibility. 771 // Deprecated: Use IdleTimeout instead. 772 MaxKeepaliveDuration time.Duration 773 774 // Whether to enable tcp keep-alive connections. 775 // 776 // Whether the operating system should send tcp keep-alive messages on the tcp connection. 777 // 778 // By default tcp keep-alive connections are disabled. 779 TCPKeepalive bool 780 781 // Period between tcp keep-alive messages. 782 // 783 // TCP keep-alive period is determined by operating system by default. 784 TCPKeepalivePeriod time.Duration 785 786 // Maximum request body size. 787 // 788 // The server rejects requests with bodies exceeding this limit. 789 // 790 // Request body size is limited by DefaultMaxRequestBodySize by default. 791 MaxRequestBodySize int 792 793 // Aggressively reduces memory usage at the cost of higher CPU usage 794 // if set to true. 795 // 796 // Try enabling this option only if the server consumes too much memory 797 // serving mostly idle keep-alive connections. This may reduce memory 798 // usage by more than 50%. 799 // 800 // Aggressive memory usage reduction is disabled by default. 801 ReduceMemoryUsage bool 802 803 // Rejects all non-GET requests if set to true. 804 // 805 // This option is useful as anti-DoS protection for servers 806 // accepting only GET requests. The request size is limited 807 // by ReadBufferSize if GetOnly is set. 808 // 809 // Server accepts all the requests by default. 810 GetOnly bool 811 812 // Logs all errors, including the most frequent 813 // 'connection reset by peer', 'broken pipe' and 'connection timeout' 814 // errors. Such errors are common in production serving real-world 815 // clients. 816 // 817 // By default the most frequent errors such as 818 // 'connection reset by peer', 'broken pipe' and 'connection timeout' 819 // are suppressed in order to limit output log traffic. 820 LogAllErrors bool 821 822 // Header names are passed as-is without normalization 823 // if this option is set. 824 // 825 // Disabled header names' normalization may be useful only for proxying 826 // incoming requests to other servers expecting case-sensitive 827 // header names. See https://github.com/valyala/fasthttp/issues/57 828 // for details. 829 // 830 // By default, request and response header names are normalized, i.e. 831 // The first letter and the first letters following dashes 832 // are uppercase, while all the other letters are lowercase. 833 // Examples: 834 // 835 // * HOST -> Host 836 // * content-type -> Content-Type 837 // * cONTENT-lenGTH -> Content-Length 838 DisableHeaderNamesNormalizing bool 839 840 // SleepWhenConcurrencyLimitsExceeded is a duration to be slept of if 841 // the concurrency limit in exceeded (default [when is 0]: don't sleep 842 // and accept new connections immediately). 843 SleepWhenConcurrencyLimitsExceeded time.Duration 844 845 // NoDefaultServerHeader, when set to true, causes the default Server header 846 // to be excluded from the Response. 847 // 848 // The default Server header value is the value of the Name field or an 849 // internal default value in its absence. With this option set to true, 850 // the only time a Server header will be sent is if a non-zero length 851 // value is explicitly provided during a request. 852 NoDefaultServerHeader bool 853 854 // NoDefaultContentType, when set to true, causes the default Content-Type 855 // header to be excluded from the Response. 856 // 857 // The default Content-Type header value is the internal default value. When 858 // set to true, the Content-Type will not be present. 859 NoDefaultContentType bool 860 861 // KeepHijackedConns is an opt-in disable of connection 862 // close by fasthttp after connections' HijackHandler returns. 863 // This allows to save goroutines, e.g. when fasthttp used to upgrade 864 // http connections to WS and connection goes to another handler, 865 // which will close it when needed. 866 KeepHijackedConns bool 867 } 868 869 const ( 870 ServerName = "SigLens" 871 ReadBufferSize = 4096 872 MaxConnsPerIP = 3000 873 MaxRequestsPerConn = 1000 874 MaxRequestBodySize = 512 * 1000 * 1000 875 Concurrency = 3000 876 ) 877 878 // DefaultIngestServerHttpConfig set fasthttp server default configuration 879 func DefaultIngestServerHttpConfig() WebConfig { 880 return WebConfig{ 881 Name: ServerName, 882 ReadBufferSize: ReadBufferSize, 883 MaxConnsPerIP: MaxConnsPerIP, 884 MaxRequestsPerConn: MaxRequestsPerConn, 885 MaxRequestBodySize: MaxRequestBodySize, // 100 << 20, // 100MB // 1000 * 4, // MaxRequestBodySize: 886 Concurrency: Concurrency, 887 } 888 } 889 890 // DefaultUIServerHttpConfig set fasthttp server default configuration 891 func DefaultUIServerHttpConfig() WebConfig { 892 return WebConfig{ 893 Name: fmt.Sprintf("%s-ws", ServerName), 894 ReadBufferSize: ReadBufferSize, 895 MaxConnsPerIP: MaxConnsPerIP, 896 MaxRequestsPerConn: MaxRequestsPerConn, 897 MaxRequestBodySize: MaxRequestBodySize, // 100 << 20, // 100MB // 1000 * 4, // MaxRequestBodySize: 898 Concurrency: Concurrency, 899 } 900 } 901 902 func ProcessGetConfig(ctx *fasthttp.RequestCtx) { 903 var httpResp utils.HttpServerResponse 904 jsonStr, err := GetRunningConfigAsJsonStr() 905 if err == nil { 906 ctx.SetStatusCode(fasthttp.StatusOK) 907 httpResp.Message = jsonStr 908 httpResp.StatusCode = fasthttp.StatusOK 909 } else { 910 ctx.SetStatusCode(fasthttp.StatusServiceUnavailable) 911 httpResp.Message = err.Error() 912 httpResp.StatusCode = fasthttp.StatusServiceUnavailable 913 } 914 utils.WriteResponse(ctx, httpResp) 915 } 916 917 func ProcessGetConfigAsJson(ctx *fasthttp.RequestCtx) { 918 ctx.SetStatusCode(fasthttp.StatusOK) 919 utils.WriteJsonResponse(ctx, &runningConfig) 920 } 921 922 func ProcessForceReadConfig(ctx *fasthttp.RequestCtx) { 923 newConfig, err := ReadConfigFile(configFilePath) 924 if err != nil { 925 log.Errorf("refreshConfig: Cannot stat config file while re-reading, err= %v", err) 926 return 927 } 928 SetConfig(newConfig) 929 ctx.SetStatusCode(fasthttp.StatusOK) 930 utils.WriteJsonResponse(ctx, &runningConfig) 931 } 932 933 func refreshConfig() { 934 for { 935 time.Sleep(MINUTES_REREAD_CONFIG * time.Minute) 936 fileInfo, err := os.Stat(configFilePath) 937 if err != nil { 938 log.Errorf("refreshConfig: Cannot stat config file while re-reading, err= %v", err) 939 continue 940 } 941 modifiedTime := fileInfo.ModTime() 942 modifiedTimeSec := uint64(modifiedTime.UTC().Unix()) 943 if modifiedTimeSec > configFileLastModified { 944 newConfig, err := ReadConfigFile(configFilePath) 945 if err != nil { 946 log.Errorf("refreshConfig: Cannot stat config file while re-reading, err= %v", err) 947 continue 948 } 949 SetConfig(newConfig) 950 configFileLastModified = modifiedTimeSec 951 } 952 } 953 } 954 955 func ProcessSetConfig(persistent bool, ctx *fasthttp.RequestCtx) { 956 var httpResp utils.HttpServerResponse 957 var reqBodyMap map[string]interface{} 958 reqBodyStr := ctx.PostBody() 959 err := json.Unmarshal([]byte(reqBodyStr), &reqBodyMap) 960 if err != nil { 961 log.Printf("Error = %v", err) 962 ctx.SetStatusCode(fasthttp.StatusBadRequest) 963 httpResp.Message = "Bad request" 964 httpResp.StatusCode = fasthttp.StatusBadRequest 965 utils.WriteResponse(ctx, httpResp) 966 return 967 } 968 err = setConfigParams(reqBodyMap) 969 if err == nil { 970 ctx.SetStatusCode(fasthttp.StatusOK) 971 httpResp.Message = "All OK" 972 httpResp.StatusCode = fasthttp.StatusOK 973 utils.WriteResponse(ctx, httpResp) 974 if persistent { 975 WriteToYamlConfig() 976 } 977 } else { 978 ctx.SetStatusCode(fasthttp.StatusForbidden) 979 httpResp.Message = err.Error() 980 httpResp.StatusCode = fasthttp.StatusForbidden 981 utils.WriteResponse(ctx, httpResp) 982 } 983 } 984 985 func setConfigParams(reqBodyMap map[string]interface{}) error { 986 for inputCfgParam := range reqBodyMap { 987 if inputCfgParam == "eventTypeKeywords" { 988 inputValueParam := reqBodyMap["eventTypeKeywords"] 989 evArray, err := extractStrArray(inputValueParam) 990 if err != nil { 991 return err 992 } 993 SetEventTypeKeywords(evArray) 994 } else { 995 err := fmt.Errorf("key = %v not allowed to update", inputCfgParam) 996 return err 997 } 998 } 999 return nil 1000 } 1001 1002 func extractStrArray(inputValueParam interface{}) ([]string, error) { 1003 switch inputValueParam.(type) { 1004 case []interface{}: 1005 break 1006 default: 1007 err := fmt.Errorf("inputValueParam type = %T not accepted", inputValueParam) 1008 return nil, err 1009 } 1010 evArray := []string{} 1011 for _, element := range inputValueParam.([]interface{}) { 1012 switch element := element.(type) { 1013 case string: 1014 str := element 1015 evArray = append(evArray, str) 1016 default: 1017 err := fmt.Errorf("element type = %T not accepted", element) 1018 return nil, err 1019 } 1020 } 1021 return evArray, nil 1022 } 1023 1024 func getQueryServerPort() (uint64, error) { 1025 if runningConfig.QueryPort == 0 { 1026 return 0, errors.New("QueryServer Port config was not specified") 1027 } 1028 return runningConfig.QueryPort, nil 1029 } 1030 1031 func GetQueryServerBaseUrl() string { 1032 hostname := GetQueryHostname() 1033 if hostname == "" { 1034 port, err := getQueryServerPort() 1035 if err != nil { 1036 return "http://localhost:5122" 1037 } 1038 return "http://localhost:" + fmt.Sprintf("%d", port) 1039 } else { 1040 if IsTlsEnabled() { 1041 hostname = "https://" + hostname 1042 } else { 1043 hostname = "http://" + hostname 1044 } 1045 return hostname 1046 } 1047 } 1048 1049 // DefaultUIServerHttpConfig set fasthttp server default configuration 1050 func DefaultQueryServerHttpConfig() WebConfig { 1051 return WebConfig{ 1052 Name: fmt.Sprintf("%s-query", ServerName), 1053 ReadBufferSize: ReadBufferSize, 1054 MaxConnsPerIP: MaxConnsPerIP, 1055 MaxRequestsPerConn: MaxRequestsPerConn, 1056 MaxRequestBodySize: MaxRequestBodySize, // 100 << 20, // 100MB // 1000 * 4, // MaxRequestBodySize: 1057 Concurrency: Concurrency, 1058 } 1059 } 1060 1061 func DefaultIngestionHttpConfig() WebConfig { 1062 return WebConfig{ 1063 Name: fmt.Sprintf("%s-ingest", ServerName), 1064 ReadBufferSize: ReadBufferSize, 1065 MaxConnsPerIP: MaxConnsPerIP, 1066 MaxRequestsPerConn: MaxRequestsPerConn, 1067 MaxRequestBodySize: MaxRequestBodySize, // 100 << 20, // 100MB // 1000 * 4, // MaxRequestBodySize: 1068 Concurrency: Concurrency, 1069 } 1070 } 1071 1072 func DefaultAddonsServerHttpConfig() WebConfig { 1073 return WebConfig{ 1074 Name: fmt.Sprintf("%s-addons", ServerName), 1075 ReadBufferSize: ReadBufferSize, 1076 MaxConnsPerIP: MaxConnsPerIP, 1077 MaxRequestsPerConn: MaxRequestsPerConn, 1078 MaxRequestBodySize: MaxRequestBodySize, 1079 Concurrency: Concurrency, 1080 } 1081 }