1
0
Fork 0
mirror of https://github.com/postmannen/ctrl.git synced 2024-12-14 12:37:31 +00:00
ctrl/configuration_flags.go

318 lines
14 KiB
Go
Raw Normal View History

package steward
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
toml "github.com/pelletier/go-toml"
)
// --- flag string slice
// flagStringSlice is a type used when a flag value contains
// comma separated values like `-myflag="somevalue,anothervalue`.
// If a flag of this type is used, and it contains a value, the
// Set(string) method will call the Parse() method.
// The comma separated content will then be split, and put into
// the []values field, and the `ok` field will be set to true, so
// it can be used to check if the flag was used and contained any
// values.
type flagNodeSlice struct {
value string
OK bool
2021-06-29 06:21:42 +00:00
Values []Node
}
2021-04-06 09:02:58 +00:00
// String method
func (f *flagNodeSlice) String() string {
return ""
}
// Set will be called when this flag type is used as a flag.Var.
// It will put the comma separated string value given as input into
// the `value`field, then call the Parse function to split those
// comma separated values into []values.
func (f *flagNodeSlice) Set(s string) error {
f.value = s
f.Parse()
return nil
}
// If the flag value "RST" is given, set the default values
// for each of the flag var's fields, and return back.
// Since we reset the actual flag values, it is also these
// blank values that will be written to the config file, and
// making the change persistent in the system. This will also
// be reflected in values stored in the config file, since the
// config file is written after the flags have been parsed.
func (f *flagNodeSlice) Parse() error {
if len(f.value) == 0 {
return nil
}
split := strings.Split(f.value, ",")
// Reset values if RST was the flag value.
if split[0] == "RST" {
f.OK = false
f.value = ""
2021-06-29 06:21:42 +00:00
f.Values = []Node{}
return nil
}
fv := f.value
sp := strings.Split(fv, ",")
f.OK = true
2021-06-29 06:21:42 +00:00
f.Values = []Node{}
for _, v := range sp {
2021-06-29 06:21:42 +00:00
f.Values = append(f.Values, Node(v))
}
return nil
}
// --- Configuration
type Configuration struct {
// The configuration folder on disk
ConfigFolder string
2021-05-12 07:50:03 +00:00
// The folder where the socket file should live
SocketFolder string
// TCP Listener for sending messages to the system
TCPListener string
2021-05-12 07:50:03 +00:00
// The folder where the database should live
DatabaseFolder string
// some unique string to identify this Edge unit
NodeName string
// the address of the message broker
BrokerAddress string
// nats connect retry
NatsConnectRetryInterval int
// The number of the profiling port
ProfilingPort string
// host and port for prometheus listener, e.g. localhost:2112
PromHostAndPort string
// set to true if this is the node that should receive the error log's from other nodes
DefaultMessageTimeout int
// default amount of retries that will be done before a message is thrown away, and out of the system
DefaultMessageRetries int
// Publisher data folder
SubscribersDataFolder string
2021-03-12 09:41:03 +00:00
// central node to receive messages published from nodes
CentralNodeName string
2021-04-19 19:06:37 +00:00
// Path to the certificate of the root CA
RootCAPath string
// Full path to the NKEY's seed file
NkeySeedFile string
2021-08-23 10:47:33 +00:00
// The host and port to expose the data folder
ExposeDataFolder string
// Timeout for error messages
ErrorMessageTimeout int
// Retries for error messages.
ErrorMessageRetries int
2021-04-05 06:37:24 +00:00
// Make the current node send hello messages to central at given interval in seconds
2021-04-06 03:46:07 +00:00
StartPubREQHello int
// Start the central error logger.
// Takes a comma separated string of nodes to receive from or "*" for all nodes.
2021-04-06 05:56:49 +00:00
StartSubREQErrorLog flagNodeSlice
// Subscriber for hello messages
2021-04-06 03:46:07 +00:00
StartSubREQHello flagNodeSlice
// Subscriber for text logging
StartSubREQToFileAppend flagNodeSlice
2021-04-06 17:42:03 +00:00
// Subscriber for writing to file
2021-04-13 15:15:13 +00:00
StartSubREQToFile flagNodeSlice
// Subscriber for Echo Request
2021-04-06 04:08:26 +00:00
StartSubREQPing flagNodeSlice
// Subscriber for Echo Reply
2021-04-06 04:08:26 +00:00
StartSubREQPong flagNodeSlice
// Subscriber for CLICommandRequest
2021-04-04 09:19:17 +00:00
StartSubREQCliCommand flagNodeSlice
// Subscriber for REQnCliCommand
StartSubREQnCliCommand flagNodeSlice
// Subscriber for REQToConsole
StartSubREQToConsole flagNodeSlice
2021-04-06 17:42:03 +00:00
// Subscriber for REQHttpGet
StartSubREQHttpGet flagNodeSlice
2021-04-13 09:28:52 +00:00
// Subscriber for tailing log files
StartSubREQTailFile flagNodeSlice
// Subscriber for continously delivery of output from cli commands.
StartSubREQnCliCommandCont flagNodeSlice
}
2021-04-06 09:02:58 +00:00
// NewConfiguration will set a default Configuration,
// and return a *Configuration.
func NewConfiguration() *Configuration {
c := Configuration{}
return &c
}
// Default configuration
func newConfigurationDefaults() Configuration {
c := Configuration{
ConfigFolder: "./etc/",
SocketFolder: "./tmp",
TCPListener: "",
DatabaseFolder: "./var/lib",
BrokerAddress: "127.0.0.1:4222",
NatsConnectRetryInterval: 10,
ProfilingPort: "",
PromHostAndPort: "",
DefaultMessageTimeout: 10,
DefaultMessageRetries: 1,
StartPubREQHello: 30,
SubscribersDataFolder: "./data",
CentralNodeName: "",
RootCAPath: "",
NkeySeedFile: "",
ExposeDataFolder: "",
ErrorMessageTimeout: 60,
ErrorMessageRetries: 10,
StartSubREQErrorLog: flagNodeSlice{Values: []Node{}},
StartSubREQHello: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQToFileAppend: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQToFile: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQPing: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQPong: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQCliCommand: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQnCliCommand: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQToConsole: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQHttpGet: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQTailFile: flagNodeSlice{OK: true, Values: []Node{"*"}},
StartSubREQnCliCommandCont: flagNodeSlice{OK: true, Values: []Node{"*"}},
}
return c
}
2021-04-06 09:02:58 +00:00
// 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")
2021-08-27 10:19:35 +00:00
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")
2021-05-12 07:50:03 +00:00
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.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")
2021-05-12 07:50:03 +00:00
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.NatsConnectRetryInterval, "natsConnectRetryInterval", fc.NatsConnectRetryInterval, "default nats retry connect interval in seconds.")
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.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")
2021-04-19 19:06:37 +00:00
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")
2021-08-23 10:47:33 +00:00
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")
2021-04-06 03:46:07 +00:00
flag.IntVar(&c.StartPubREQHello, "startPubREQHello", fc.StartPubREQHello, "Make the current node send hello messages to central at given interval in seconds")
2021-04-06 05:56:49 +00:00
flag.Var(&c.StartSubREQErrorLog, "startSubREQErrorLog", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
2021-04-06 03:46:07 +00:00
flag.Var(&c.StartSubREQHello, "startSubREQHello", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
flag.Var(&c.StartSubREQToFileAppend, "startSubREQToFileAppend", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
2021-04-13 15:15:13 +00:00
flag.Var(&c.StartSubREQToFile, "startSubREQToFile", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
2021-04-06 04:08:26 +00:00
flag.Var(&c.StartSubREQPing, "startSubREQPing", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
flag.Var(&c.StartSubREQPong, "startSubREQPong", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
2021-04-04 09:19:17 +00:00
flag.Var(&c.StartSubREQCliCommand, "startSubREQCliCommand", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
flag.Var(&c.StartSubREQnCliCommand, "startSubREQnCliCommand", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
flag.Var(&c.StartSubREQToConsole, "startSubREQToConsole", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
2021-04-06 17:42:03 +00:00
flag.Var(&c.StartSubREQHttpGet, "startSubREQHttpGet", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
2021-04-13 09:28:52 +00:00
flag.Var(&c.StartSubREQTailFile, "startSubREQTailFile", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
flag.Var(&c.StartSubREQnCliCommandCont, "startSubREQnCliCommandCont", "Specify comma separated list for nodes to allow messages from. Use \"*\" for from all. Value RST will turn off subscriber.")
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)
}
return nil
}
// Reads the current config file from disk.
func (c *Configuration) ReadConfigFile(configFolder string) (Configuration, error) {
fp := filepath.Join(configFolder, "config.toml")
if _, err := os.Stat(fp); os.IsNotExist(err) {
return Configuration{}, fmt.Errorf("error: no config file found %v: %v", fp, err)
}
f, err := os.OpenFile(fp, os.O_RDONLY, 0600)
if err != nil {
return Configuration{}, fmt.Errorf("error: ReadConfigFile: failed to open file: %v", err)
}
defer f.Close()
var conf Configuration
dec := toml.NewDecoder(f)
err = dec.Decode(&conf)
if err != nil {
return Configuration{}, fmt.Errorf("error: decode toml file %v: %v", fp, err)
}
// fmt.Printf("%+v\n", c)
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) {
2021-05-12 07:50:03 +00:00
err := os.MkdirAll(c.ConfigFolder, 0700)
if err != nil {
2021-05-20 10:27:25 +00:00
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, 0600)
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
}