package steward import ( "flag" "fmt" "log" "os" "path/filepath" toml "github.com/pelletier/go-toml/v2" ) // Configuration are the structure that holds all the different // configuration options used both with flags and the config file. // If a new field is added to this struct there should also be // added the same field to the ConfigurationFromFile struct, and // an if check should be added to the checkConfigValues function // to set default values when reading from config file. type Configuration struct { // RingBufferPersistStore, enable or disable the persisting of // messages being processed to local db. RingBufferPersistStore bool `comment:"RingBufferPersistStore, enable or disable the persisting of messages being processed to local db"` // RingBufferSize RingBufferSize int `comment:"RingBufferSize"` // ConfigFolder, the location for the configuration folder on disk ConfigFolder string `comment:"ConfigFolder, the location for the configuration folder on disk"` // The folder where the socket file should live SocketFolder string `comment:"The folder where the socket file should live"` // The folder where the readfolder should live ReadFolder string `comment:"The folder where the readfolder should live"` // EnableReadFolder for enabling the read messages api from readfolder EnableReadFolder bool `comment:"EnableReadFolder for enabling the read messages api from readfolder"` // TCP Listener for sending messages to the system, : TCPListener string `comment:"TCP Listener for sending messages to the system, :"` // HTTP Listener for sending messages to the system, : HTTPListener string `comment:"HTTP Listener for sending messages to the system, :"` // The folder where the database should live DatabaseFolder string `comment:"The folder where the database should live"` // Unique string to identify this Edge unit NodeName string `comment:"Unique string to identify this Edge unit"` // The address of the message broker,
: BrokerAddress string `comment:"The address of the message broker,
:"` // NatsConnOptTimeout the timeout for trying the connect to nats broker NatsConnOptTimeout int `comment:"NatsConnOptTimeout the timeout for trying the connect to nats broker"` // Nats connect retry interval in seconds NatsConnectRetryInterval int `comment:"Nats connect retry interval in seconds"` // NatsReconnectJitter in milliseconds NatsReconnectJitter int `comment:"NatsReconnectJitter in milliseconds"` // NatsReconnectJitterTLS in seconds NatsReconnectJitterTLS int `comment:"NatsReconnectJitterTLS in seconds"` // REQKeysRequestUpdateInterval in seconds REQKeysRequestUpdateInterval int `comment:"REQKeysRequestUpdateInterval in seconds"` // REQAclRequestUpdateInterval in seconds REQAclRequestUpdateInterval int `comment:"REQAclRequestUpdateInterval in seconds"` // The number of the profiling port ProfilingPort string `comment:"The number of the profiling port"` // Host and port for prometheus listener, e.g. localhost:2112 PromHostAndPort string `comment:"Host and port for prometheus listener, e.g. localhost:2112"` // Set to true if this is the node that should receive the error log's from other nodes DefaultMessageTimeout int `comment:"Set to true if this is the node that should receive the error log's from other nodes"` // Default value for how long can a request method max be allowed to run in seconds DefaultMethodTimeout int `comment:"Default value for how long can a request method max be allowed to run in seconds"` // Default amount of retries that will be done before a message is thrown away, and out of the system DefaultMessageRetries int `comment:"Default amount of retries that will be done before a message is thrown away, and out of the system"` // The path to the data folder SubscribersDataFolder string `comment:"The path to the data folder"` // Name of central node to receive logs, errors, key/acl handling CentralNodeName string `comment:"Name of central node to receive logs, errors, key/acl handling"` // The full path to the certificate of the root CA RootCAPath string `comment:"The full path to the certificate of the root CA"` // Full path to the NKEY's seed file NkeySeedFile string `comment:"Full path to the NKEY's seed file"` // The full path to the NKEY user file NkeyPublicKey string `toml:"-"` // The host and port to expose the data folder, : ExposeDataFolder string `comment:"The host and port to expose the data folder, :"` // Timeout in seconds for error messages ErrorMessageTimeout int `comment:"Timeout in seconds for error messages"` // Retries for error messages ErrorMessageRetries int `comment:"Retries for error messages"` // Compression z for zstd or g for gzip Compression string `comment:"Compression z for zstd or g for gzip"` // Serialization, supports cbor or gob,default is gob. Enable cbor by setting the string value cbor Serialization string `comment:"Serialization, supports cbor or gob,default is gob. Enable cbor by setting the string value cbor"` // SetBlockProfileRate for block profiling SetBlockProfileRate int `comment:"SetBlockProfileRate for block profiling"` // EnableSocket for enabling the creation of a steward.sock file EnableSocket bool `comment:"EnableSocket for enabling the creation of a steward.sock file"` // EnableTUI will enable the Terminal User Interface EnableTUI bool `comment:"EnableTUI will enable the Terminal User Interface"` // EnableSignatureCheck to enable signature checking EnableSignatureCheck bool `comment:"EnableSignatureCheck to enable signature checking"` // EnableAclCheck to enable ACL checking EnableAclCheck bool `comment:"EnableAclCheck to enable ACL checking"` // IsCentralAuth, enable to make this instance take the role as the central auth server IsCentralAuth bool `comment:"IsCentralAuth, enable to make this instance take the role as the central auth server"` // EnableDebug will also enable printing all the messages received in the errorKernel to STDERR. EnableDebug bool `comment:"EnableDebug will also enable printing all the messages received in the errorKernel to STDERR."` // LogLevel LogLevel string `comment:"LogLevel info/debug/none."` LogConsoleTimestamps bool `comment:"LogConsoleTimestamps true/false for enabling or disabling timestamps when printing errors and information to stderr"` // KeepPublishersAliveFor number of seconds // Timer that will be used for when to remove the sub process // publisher. The timer is reset each time a message is published with // the process, so the sub process publisher will not be removed until // it have not received any messages for the given amount of time. KeepPublishersAliveFor int `comment:"KeepPublishersAliveFor number of seconds Timer that will be used for when to remove the sub process publisher. The timer is reset each time a message is published with the process, so the sub process publisher will not be removed until it have not received any messages for the given amount of time."` // StartPubREQHello, sets the interval in seconds for how often we send hello messages to central server StartPubREQHello int `comment:"StartPubREQHello, sets the interval in seconds for how often we send hello messages to central server"` // Enable the updates of public keys EnableKeyUpdates bool `comment:"Enable the updates of public keys"` // Enable the updates of acl's EnableAclUpdates bool `comment:"Enable the updates of acl's"` // Start the central error logger. IsCentralErrorLogger bool `comment:"Start the central error logger."` // Start subscriber for hello messages StartSubREQHello bool `comment:"Start subscriber for hello messages"` // Start subscriber for text logging StartSubREQToFileAppend bool `comment:"Start subscriber for text logging"` // Start subscriber for writing to file StartSubREQToFile bool `comment:"Start subscriber for writing to file"` // Start subscriber for writing to file without ACK StartSubREQToFileNACK bool `comment:"Start subscriber for writing to file without ACK"` // Start subscriber for reading files to copy StartSubREQCopySrc bool `comment:"Start subscriber for reading files to copy"` // Start subscriber for writing copied files to disk StartSubREQCopyDst bool `comment:"Start subscriber for writing copied files to disk"` // Start subscriber for Echo Request StartSubREQPing bool `comment:"Start subscriber for Echo Request"` // Start subscriber for Echo Reply StartSubREQPong bool `comment:"Start subscriber for Echo Reply"` // Start subscriber for CLICommandRequest StartSubREQCliCommand bool `comment:"Start subscriber for CLICommandRequest"` // Start subscriber for REQToConsole StartSubREQToConsole bool `comment:"Start subscriber for REQToConsole"` // Start subscriber for REQHttpGet StartSubREQHttpGet bool `comment:"Start subscriber for REQHttpGet"` // Start subscriber for REQHttpGetScheduled StartSubREQHttpGetScheduled bool `comment:"Start subscriber for REQHttpGetScheduled"` // Start subscriber for tailing log files StartSubREQTailFile bool `comment:"Start subscriber for tailing log files"` // Start subscriber for continously delivery of output from cli commands. StartSubREQCliCommandCont bool `comment:"Start subscriber for continously delivery of output from cli commands."` } // ConfigurationFromFile should have the same structure as // Configuration. This structure is used when parsing the // configuration values from file, so we are able to detect // if a value were given or not when parsing. type ConfigurationFromFile struct { ConfigFolder *string RingBufferPersistStore *bool RingBufferSize *int SocketFolder *string ReadFolder *string EnableReadFolder *bool TCPListener *string HTTPListener *string DatabaseFolder *string NodeName *string BrokerAddress *string NatsConnOptTimeout *int NatsConnectRetryInterval *int NatsReconnectJitter *int NatsReconnectJitterTLS *int REQKeysRequestUpdateInterval *int REQAclRequestUpdateInterval *int ProfilingPort *string PromHostAndPort *string DefaultMessageTimeout *int DefaultMessageRetries *int DefaultMethodTimeout *int SubscribersDataFolder *string CentralNodeName *string RootCAPath *string NkeySeedFile *string ExposeDataFolder *string ErrorMessageTimeout *int ErrorMessageRetries *int Compression *string Serialization *string SetBlockProfileRate *int EnableSocket *bool EnableTUI *bool EnableSignatureCheck *bool EnableAclCheck *bool IsCentralAuth *bool EnableDebug *bool LogLevel *string LogConsoleTimestamps *bool KeepPublishersAliveFor *int StartPubREQHello *int EnableKeyUpdates *bool EnableAclUpdates *bool IsCentralErrorLogger *bool StartSubREQHello *bool StartSubREQToFileAppend *bool StartSubREQToFile *bool StartSubREQToFileNACK *bool StartSubREQCopySrc *bool StartSubREQCopyDst *bool StartSubREQPing *bool StartSubREQPong *bool StartSubREQCliCommand *bool StartSubREQToConsole *bool StartSubREQHttpGet *bool StartSubREQHttpGetScheduled *bool StartSubREQTailFile *bool StartSubREQCliCommandCont *bool } // NewConfiguration will return a *Configuration. func NewConfiguration() *Configuration { c := Configuration{} return &c } // Get a Configuration struct with the default values set. func newConfigurationDefaults() Configuration { c := Configuration{ ConfigFolder: "./etc/", RingBufferPersistStore: true, RingBufferSize: 1000, SocketFolder: "./tmp", ReadFolder: "./readfolder", EnableReadFolder: true, TCPListener: "", HTTPListener: "", DatabaseFolder: "./var/lib", NodeName: "", BrokerAddress: "127.0.0.1:4222", NatsConnOptTimeout: 20, NatsConnectRetryInterval: 10, NatsReconnectJitter: 100, NatsReconnectJitterTLS: 1, REQKeysRequestUpdateInterval: 60, REQAclRequestUpdateInterval: 60, ProfilingPort: "", PromHostAndPort: "", DefaultMessageTimeout: 10, DefaultMessageRetries: 1, DefaultMethodTimeout: 10, SubscribersDataFolder: "./data", CentralNodeName: "", RootCAPath: "", NkeySeedFile: "", ExposeDataFolder: "", ErrorMessageTimeout: 60, ErrorMessageRetries: 10, Compression: "", Serialization: "", SetBlockProfileRate: 0, EnableSocket: true, EnableTUI: false, EnableSignatureCheck: false, EnableAclCheck: false, IsCentralAuth: false, EnableDebug: false, LogLevel: "none", LogConsoleTimestamps: false, KeepPublishersAliveFor: 10, StartPubREQHello: 30, EnableKeyUpdates: true, EnableAclUpdates: true, IsCentralErrorLogger: false, StartSubREQHello: true, StartSubREQToFileAppend: true, StartSubREQToFile: true, StartSubREQToFileNACK: true, StartSubREQCopySrc: true, StartSubREQCopyDst: true, StartSubREQPing: true, StartSubREQPong: true, StartSubREQCliCommand: true, StartSubREQToConsole: true, StartSubREQHttpGet: true, StartSubREQHttpGetScheduled: true, StartSubREQTailFile: true, StartSubREQCliCommandCont: true, } return c } // Check if all values are present in config file, and if not // found use the default value. func checkConfigValues(cf ConfigurationFromFile) Configuration { var conf Configuration cd := newConfigurationDefaults() if cf.RingBufferSize == nil { conf.RingBufferSize = cd.RingBufferSize } else { conf.RingBufferSize = *cf.RingBufferSize } if cf.RingBufferPersistStore == nil { conf.RingBufferPersistStore = cd.RingBufferPersistStore } else { conf.RingBufferPersistStore = *cf.RingBufferPersistStore } if cf.ConfigFolder == nil { conf.ConfigFolder = cd.ConfigFolder } else { conf.ConfigFolder = *cf.ConfigFolder } if cf.SocketFolder == nil { conf.SocketFolder = cd.SocketFolder } else { conf.SocketFolder = *cf.SocketFolder } if cf.ReadFolder == nil { conf.ReadFolder = cd.ReadFolder } else { conf.ReadFolder = *cf.ReadFolder } if cf.EnableReadFolder == nil { conf.EnableReadFolder = cd.EnableReadFolder } else { conf.EnableReadFolder = *cf.EnableReadFolder } if cf.TCPListener == nil { conf.TCPListener = cd.TCPListener } else { conf.TCPListener = *cf.TCPListener } if cf.HTTPListener == nil { conf.HTTPListener = cd.HTTPListener } else { conf.HTTPListener = *cf.HTTPListener } if cf.DatabaseFolder == nil { conf.DatabaseFolder = cd.DatabaseFolder } else { conf.DatabaseFolder = *cf.DatabaseFolder } if cf.NodeName == nil { conf.NodeName = cd.NodeName } else { conf.NodeName = *cf.NodeName } if cf.BrokerAddress == nil { conf.BrokerAddress = cd.BrokerAddress } else { conf.BrokerAddress = *cf.BrokerAddress } if cf.NatsConnOptTimeout == nil { conf.NatsConnOptTimeout = cd.NatsConnOptTimeout } else { conf.NatsConnOptTimeout = *cf.NatsConnOptTimeout } if cf.NatsConnectRetryInterval == nil { conf.NatsConnectRetryInterval = cd.NatsConnectRetryInterval } else { conf.NatsConnectRetryInterval = *cf.NatsConnectRetryInterval } if cf.NatsReconnectJitter == nil { conf.NatsReconnectJitter = cd.NatsReconnectJitter } else { conf.NatsReconnectJitter = *cf.NatsReconnectJitter } if cf.NatsReconnectJitterTLS == nil { conf.NatsReconnectJitterTLS = cd.NatsReconnectJitterTLS } else { conf.NatsReconnectJitterTLS = *cf.NatsReconnectJitterTLS } if cf.REQKeysRequestUpdateInterval == nil { conf.REQKeysRequestUpdateInterval = cd.REQKeysRequestUpdateInterval } else { conf.REQKeysRequestUpdateInterval = *cf.REQKeysRequestUpdateInterval } if cf.REQAclRequestUpdateInterval == nil { conf.REQAclRequestUpdateInterval = cd.REQAclRequestUpdateInterval } else { conf.REQAclRequestUpdateInterval = *cf.REQAclRequestUpdateInterval } if cf.ProfilingPort == nil { conf.ProfilingPort = cd.ProfilingPort } else { conf.ProfilingPort = *cf.ProfilingPort } if cf.PromHostAndPort == nil { conf.PromHostAndPort = cd.PromHostAndPort } else { conf.PromHostAndPort = *cf.PromHostAndPort } if cf.DefaultMessageTimeout == nil { conf.DefaultMessageTimeout = cd.DefaultMessageTimeout } else { conf.DefaultMessageTimeout = *cf.DefaultMessageTimeout } if cf.DefaultMessageRetries == nil { conf.DefaultMessageRetries = cd.DefaultMessageRetries } else { conf.DefaultMessageRetries = *cf.DefaultMessageRetries } if cf.DefaultMethodTimeout == nil { conf.DefaultMethodTimeout = cd.DefaultMethodTimeout } else { conf.DefaultMethodTimeout = *cf.DefaultMethodTimeout } if cf.SubscribersDataFolder == nil { conf.SubscribersDataFolder = cd.SubscribersDataFolder } else { conf.SubscribersDataFolder = *cf.SubscribersDataFolder } if cf.CentralNodeName == nil { conf.CentralNodeName = cd.CentralNodeName } else { conf.CentralNodeName = *cf.CentralNodeName } if cf.RootCAPath == nil { conf.RootCAPath = cd.RootCAPath } else { conf.RootCAPath = *cf.RootCAPath } if cf.NkeySeedFile == nil { conf.NkeySeedFile = cd.NkeySeedFile } else { conf.NkeySeedFile = *cf.NkeySeedFile } if cf.ExposeDataFolder == nil { conf.ExposeDataFolder = cd.ExposeDataFolder } else { conf.ExposeDataFolder = *cf.ExposeDataFolder } if cf.ErrorMessageTimeout == nil { conf.ErrorMessageTimeout = cd.ErrorMessageTimeout } else { conf.ErrorMessageTimeout = *cf.ErrorMessageTimeout } if cf.ErrorMessageRetries == nil { conf.ErrorMessageRetries = cd.ErrorMessageRetries } else { conf.ErrorMessageRetries = *cf.ErrorMessageRetries } if cf.Compression == nil { conf.Compression = cd.Compression } else { conf.Compression = *cf.Compression } if cf.Serialization == nil { conf.Serialization = cd.Serialization } else { conf.Serialization = *cf.Serialization } if cf.SetBlockProfileRate == nil { conf.SetBlockProfileRate = cd.SetBlockProfileRate } else { conf.SetBlockProfileRate = *cf.SetBlockProfileRate } if cf.EnableSocket == nil { conf.EnableSocket = cd.EnableSocket } else { conf.EnableSocket = *cf.EnableSocket } if cf.EnableTUI == nil { conf.EnableTUI = cd.EnableTUI } else { conf.EnableTUI = *cf.EnableTUI } if cf.EnableSignatureCheck == nil { conf.EnableSignatureCheck = cd.EnableSignatureCheck } else { conf.EnableSignatureCheck = *cf.EnableSignatureCheck } if cf.EnableAclCheck == nil { conf.EnableAclCheck = cd.EnableAclCheck } else { conf.EnableAclCheck = *cf.EnableAclCheck } if cf.IsCentralAuth == nil { conf.IsCentralAuth = cd.IsCentralAuth } else { conf.IsCentralAuth = *cf.IsCentralAuth } if cf.EnableDebug == nil { conf.EnableDebug = cd.EnableDebug } else { conf.EnableDebug = *cf.EnableDebug } if cf.LogLevel == nil { conf.LogLevel = cd.LogLevel } else { conf.LogLevel = *cf.LogLevel } if cf.LogConsoleTimestamps == nil { conf.LogConsoleTimestamps = cd.LogConsoleTimestamps } else { conf.LogConsoleTimestamps = *cf.LogConsoleTimestamps } if cf.KeepPublishersAliveFor == nil { conf.KeepPublishersAliveFor = cd.KeepPublishersAliveFor } else { conf.KeepPublishersAliveFor = *cf.KeepPublishersAliveFor } // --- Start pub/sub if cf.StartPubREQHello == nil { conf.StartPubREQHello = cd.StartPubREQHello } else { conf.StartPubREQHello = *cf.StartPubREQHello } if cf.EnableKeyUpdates == nil { conf.EnableKeyUpdates = cd.EnableKeyUpdates } else { conf.EnableKeyUpdates = *cf.EnableKeyUpdates } if cf.EnableAclUpdates == nil { conf.EnableAclUpdates = cd.EnableAclUpdates } else { conf.EnableAclUpdates = *cf.EnableAclUpdates } if cf.IsCentralErrorLogger == nil { conf.IsCentralErrorLogger = cd.IsCentralErrorLogger } else { conf.IsCentralErrorLogger = *cf.IsCentralErrorLogger } if cf.StartSubREQHello == nil { conf.StartSubREQHello = cd.StartSubREQHello } else { conf.StartSubREQHello = *cf.StartSubREQHello } if cf.StartSubREQToFileAppend == nil { conf.StartSubREQToFileAppend = cd.StartSubREQToFileAppend } else { conf.StartSubREQToFileAppend = *cf.StartSubREQToFileAppend } if cf.StartSubREQToFile == nil { conf.StartSubREQToFile = cd.StartSubREQToFile } else { conf.StartSubREQToFile = *cf.StartSubREQToFile } if cf.StartSubREQToFileNACK == nil { conf.StartSubREQToFileNACK = cd.StartSubREQToFileNACK } else { conf.StartSubREQToFileNACK = *cf.StartSubREQToFileNACK } if cf.StartSubREQCopySrc == nil { conf.StartSubREQCopySrc = cd.StartSubREQCopySrc } else { conf.StartSubREQCopySrc = *cf.StartSubREQCopySrc } if cf.StartSubREQCopyDst == nil { conf.StartSubREQCopyDst = cd.StartSubREQCopyDst } else { conf.StartSubREQCopyDst = *cf.StartSubREQCopyDst } if cf.StartSubREQPing == nil { conf.StartSubREQPing = cd.StartSubREQPing } else { conf.StartSubREQPing = *cf.StartSubREQPing } if cf.StartSubREQPong == nil { conf.StartSubREQPong = cd.StartSubREQPong } else { conf.StartSubREQPong = *cf.StartSubREQPong } if cf.StartSubREQCliCommand == nil { conf.StartSubREQCliCommand = cd.StartSubREQCliCommand } else { conf.StartSubREQCliCommand = *cf.StartSubREQCliCommand } if cf.StartSubREQToConsole == nil { conf.StartSubREQToConsole = cd.StartSubREQToConsole } else { conf.StartSubREQToConsole = *cf.StartSubREQToConsole } if cf.StartSubREQHttpGet == nil { conf.StartSubREQHttpGet = cd.StartSubREQHttpGet } else { conf.StartSubREQHttpGet = *cf.StartSubREQHttpGet } if cf.StartSubREQHttpGetScheduled == nil { conf.StartSubREQHttpGetScheduled = cd.StartSubREQHttpGetScheduled } else { conf.StartSubREQHttpGetScheduled = *cf.StartSubREQHttpGetScheduled } if cf.StartSubREQTailFile == nil { conf.StartSubREQTailFile = cd.StartSubREQTailFile } else { conf.StartSubREQTailFile = *cf.StartSubREQTailFile } if cf.StartSubREQCliCommandCont == nil { conf.StartSubREQCliCommandCont = cd.StartSubREQCliCommandCont } else { conf.StartSubREQCliCommandCont = *cf.StartSubREQCliCommandCont } return conf } // CheckFlags will parse all flags func (c *Configuration) CheckFlags() error { // Create an empty default config var fc Configuration // Set default configfolder if no env was provided. configFolder := os.Getenv("CONFIG_FOLDER") if configFolder == "" { configFolder = "./etc/" } // Read file config. Set system default if it can't find config file. fc, err := c.ReadConfigFile(configFolder) if err != nil { log.Printf("%v\n", err) fc = newConfigurationDefaults() } if configFolder == "" { fc.ConfigFolder = "./etc/" } else { fc.ConfigFolder = configFolder } *c = fc //flag.StringVar(&c.ConfigFolder, "configFolder", fc.ConfigFolder, "Defaults to ./usr/local/steward/etc/. *NB* This flag is not used, if your config file are located somwhere else than default set the location in an env variable named CONFIGFOLDER") flag.BoolVar(&c.RingBufferPersistStore, "ringBufferPersistStore", fc.RingBufferPersistStore, "true/false for enabling the persisting of ringbuffer to disk") flag.IntVar(&c.RingBufferSize, "ringBufferSize", fc.RingBufferSize, "size of the ringbuffer") flag.StringVar(&c.SocketFolder, "socketFolder", fc.SocketFolder, "folder who contains the socket file. Defaults to ./tmp/. If other folder is used this flag must be specified at startup.") flag.StringVar(&c.ReadFolder, "readFolder", fc.ReadFolder, "folder who contains the readfolder. Defaults to ./readfolder/. If other folder is used this flag must be specified at startup.") flag.StringVar(&c.TCPListener, "tcpListener", fc.TCPListener, "start up a TCP listener in addition to the Unix Socket, to give messages to the system. e.g. localhost:8888. No value means not to start the listener, which is default. NB: You probably don't want to start this on any other interface than localhost") flag.StringVar(&c.HTTPListener, "httpListener", fc.HTTPListener, "start up a HTTP listener in addition to the Unix Socket, to give messages to the system. e.g. localhost:8888. No value means not to start the listener, which is default. NB: You probably don't want to start this on any other interface than localhost") flag.StringVar(&c.DatabaseFolder, "databaseFolder", fc.DatabaseFolder, "folder who contains the database file. Defaults to ./var/lib/. If other folder is used this flag must be specified at startup.") flag.StringVar(&c.NodeName, "nodeName", fc.NodeName, "some unique string to identify this Edge unit") flag.StringVar(&c.BrokerAddress, "brokerAddress", fc.BrokerAddress, "the address of the message broker") flag.IntVar(&c.NatsConnOptTimeout, "natsConnOptTimeout", fc.NatsConnOptTimeout, "default nats client conn timeout in seconds") flag.IntVar(&c.NatsConnectRetryInterval, "natsConnectRetryInterval", fc.NatsConnectRetryInterval, "default nats retry connect interval in seconds.") flag.IntVar(&c.NatsReconnectJitter, "natsReconnectJitter", fc.NatsReconnectJitter, "default nats ReconnectJitter interval in milliseconds.") flag.IntVar(&c.NatsReconnectJitterTLS, "natsReconnectJitterTLS", fc.NatsReconnectJitterTLS, "default nats ReconnectJitterTLS interval in seconds.") flag.IntVar(&c.REQKeysRequestUpdateInterval, "REQKeysRequestUpdateInterval", fc.REQKeysRequestUpdateInterval, "default interval in seconds for asking the central for public keys") flag.IntVar(&c.REQAclRequestUpdateInterval, "REQAclRequestUpdateInterval", fc.REQAclRequestUpdateInterval, "default interval in seconds for asking the central for acl updates") flag.StringVar(&c.ProfilingPort, "profilingPort", fc.ProfilingPort, "The number of the profiling port") flag.StringVar(&c.PromHostAndPort, "promHostAndPort", fc.PromHostAndPort, "host and port for prometheus listener, e.g. localhost:2112") flag.IntVar(&c.DefaultMessageTimeout, "defaultMessageTimeout", fc.DefaultMessageTimeout, "default message timeout in seconds. This can be overridden on the message level") flag.IntVar(&c.DefaultMessageRetries, "defaultMessageRetries", fc.DefaultMessageRetries, "default amount of retries that will be done before a message is thrown away, and out of the system") flag.IntVar(&c.DefaultMethodTimeout, "defaultMethodTimeout", fc.DefaultMethodTimeout, "default amount of seconds a request method max will be allowed to run") flag.StringVar(&c.SubscribersDataFolder, "subscribersDataFolder", fc.SubscribersDataFolder, "The data folder where subscribers are allowed to write their data if needed") flag.StringVar(&c.CentralNodeName, "centralNodeName", fc.CentralNodeName, "The name of the central node to receive messages published by this node") flag.StringVar(&c.RootCAPath, "rootCAPath", fc.RootCAPath, "If TLS, enter the path for where to find the root CA certificate") flag.StringVar(&c.NkeySeedFile, "nkeySeedFile", fc.NkeySeedFile, "The full path of the nkeys seed file") flag.StringVar(&c.ExposeDataFolder, "exposeDataFolder", fc.ExposeDataFolder, "If set the data folder will be exposed on the given host:port. Default value is not exposed at all") flag.IntVar(&c.ErrorMessageTimeout, "errorMessageTimeout", fc.ErrorMessageTimeout, "The number of seconds to wait for an error message to time out") flag.IntVar(&c.ErrorMessageRetries, "errorMessageRetries", fc.ErrorMessageRetries, "The number of if times to retry an error message before we drop it") flag.StringVar(&c.Compression, "compression", fc.Compression, "compression method to use. defaults to no compression, z = zstd, g = gzip. Undefined value will default to no compression") flag.StringVar(&c.Serialization, "serialization", fc.Serialization, "Serialization method to use. defaults to gob, other values are = cbor. Undefined value will default to gob") flag.IntVar(&c.SetBlockProfileRate, "setBlockProfileRate", fc.SetBlockProfileRate, "Enable block profiling by setting the value to f.ex. 1. 0 = disabled") flag.BoolVar(&c.EnableSocket, "enableSocket", fc.EnableSocket, "true/false, for enabling the creation of a steward.sock file") flag.BoolVar(&c.EnableTUI, "enableTUI", fc.EnableTUI, "true/false for enabling the Terminal User Interface") flag.BoolVar(&c.EnableSignatureCheck, "enableSignatureCheck", fc.EnableSignatureCheck, "true/false *TESTING* enable signature checking.") flag.BoolVar(&c.EnableAclCheck, "enableAclCheck", fc.EnableAclCheck, "true/false *TESTING* enable Acl checking.") flag.BoolVar(&c.IsCentralAuth, "isCentralAuth", fc.IsCentralAuth, "true/false, *TESTING* is this the central auth server") flag.BoolVar(&c.EnableDebug, "enableDebug", fc.EnableDebug, "true/false, will enable debug logging so all messages sent to the errorKernel will also be printed to STDERR") flag.StringVar(&c.LogLevel, "logLevel", fc.LogLevel, "info/debug") flag.BoolVar(&c.LogConsoleTimestamps, "LogConsoleTimestamps", fc.LogConsoleTimestamps, "true/false for enabling or disabling timestamps when printing errors and information to stderr") flag.IntVar(&c.KeepPublishersAliveFor, "keepPublishersAliveFor", fc.KeepPublishersAliveFor, "The amount of time we allow a publisher to stay alive without receiving any messages to publish") // Start of Request publishers/subscribers flag.IntVar(&c.StartPubREQHello, "startPubREQHello", fc.StartPubREQHello, "Make the current node send hello messages to central at given interval in seconds") flag.BoolVar(&c.EnableKeyUpdates, "EnableKeyUpdates", fc.EnableKeyUpdates, "true/false") flag.BoolVar(&c.EnableAclUpdates, "EnableAclUpdates", fc.EnableAclUpdates, "true/false") flag.BoolVar(&c.IsCentralErrorLogger, "isCentralErrorLogger", fc.IsCentralErrorLogger, "true/false") flag.BoolVar(&c.StartSubREQHello, "startSubREQHello", fc.StartSubREQHello, "true/false") flag.BoolVar(&c.StartSubREQToFileAppend, "startSubREQToFileAppend", fc.StartSubREQToFileAppend, "true/false") flag.BoolVar(&c.StartSubREQToFile, "startSubREQToFile", fc.StartSubREQToFile, "true/false") flag.BoolVar(&c.StartSubREQToFileNACK, "startSubREQToFileNACK", fc.StartSubREQToFileNACK, "true/false") flag.BoolVar(&c.StartSubREQCopySrc, "startSubREQCopySrc", fc.StartSubREQCopySrc, "true/false") flag.BoolVar(&c.StartSubREQCopyDst, "startSubREQCopyDst", fc.StartSubREQCopyDst, "true/false") flag.BoolVar(&c.StartSubREQPing, "startSubREQPing", fc.StartSubREQPing, "true/false") flag.BoolVar(&c.StartSubREQPong, "startSubREQPong", fc.StartSubREQPong, "true/false") flag.BoolVar(&c.StartSubREQCliCommand, "startSubREQCliCommand", fc.StartSubREQCliCommand, "true/false") flag.BoolVar(&c.StartSubREQToConsole, "startSubREQToConsole", fc.StartSubREQToConsole, "true/false") flag.BoolVar(&c.StartSubREQHttpGet, "startSubREQHttpGet", fc.StartSubREQHttpGet, "true/false") flag.BoolVar(&c.StartSubREQHttpGetScheduled, "startSubREQHttpGetScheduled", fc.StartSubREQHttpGetScheduled, "true/false") flag.BoolVar(&c.StartSubREQTailFile, "startSubREQTailFile", fc.StartSubREQTailFile, "true/false") flag.BoolVar(&c.StartSubREQCliCommandCont, "startSubREQCliCommandCont", fc.StartSubREQCliCommandCont, "true/false") purgeBufferDB := flag.Bool("purgeBufferDB", false, "true/false, purge the incoming buffer db and all it's state") flag.Parse() // Check that mandatory flag values have been set. switch { case c.NodeName == "": return fmt.Errorf("error: the nodeName config option or flag cannot be empty, check -help") case c.CentralNodeName == "": return fmt.Errorf("error: the centralNodeName config option or flag cannot be empty, check -help") } if err := c.WriteConfigFile(); err != nil { log.Printf("error: checkFlags: failed writing config file: %v\n", err) os.Exit(1) } if *purgeBufferDB { fp := filepath.Join(c.DatabaseFolder, "incomingBuffer.db") err := os.Remove(fp) if err != nil { log.Printf("error: failed to purge buffer state database: %v\n", err) } } return nil } // Reads the current config file from disk. func (c *Configuration) ReadConfigFile(configFolder string) (Configuration, error) { fPath := filepath.Join(configFolder, "config.toml") if _, err := os.Stat(fPath); os.IsNotExist(err) { return Configuration{}, fmt.Errorf("error: no config file found %v: %v", fPath, err) } f, err := os.OpenFile(fPath, os.O_RDONLY, 0660) if err != nil { return Configuration{}, fmt.Errorf("error: ReadConfigFile: failed to open file: %v", err) } defer f.Close() var cFile ConfigurationFromFile dec := toml.NewDecoder(f) err = dec.Decode(&cFile) if err != nil { log.Printf("error: decoding config.toml file. The program will automatically try to correct the problem, and use sane default where it kind find a value to use, but beware of this error in case the program start to behave in not expected ways: path=%v: err=%v", fPath, err) } // Check that all values read are ok. conf := checkConfigValues(cFile) return conf, nil } // WriteConfigFile will write the current config to file. If the file or the // directory for the config file does not exist it will be created. func (c *Configuration) WriteConfigFile() error { if _, err := os.Stat(c.ConfigFolder); os.IsNotExist(err) { err := os.MkdirAll(c.ConfigFolder, 0770) if err != nil { return fmt.Errorf("error: failed to create config directory %v: %v", c.ConfigFolder, err) } } fp := filepath.Join(c.ConfigFolder, "config.toml") f, err := os.OpenFile(fp, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0660) if err != nil { return fmt.Errorf("error: WriteConfigFile: failed to open file: %v", err) } defer f.Close() enc := toml.NewEncoder(f) enc.Encode(c) return nil }