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  }