296 lines
8.7 KiB
Nim
296 lines
8.7 KiB
Nim
import std/options
|
|
import std/os
|
|
import std/sequtils
|
|
import std/strutils
|
|
import std/sugar
|
|
import std/tables
|
|
from pkg/chronicles import LogLevel
|
|
import pkg/codex/conf
|
|
import pkg/codex/units
|
|
import pkg/confutils
|
|
import pkg/confutils/defs
|
|
import libp2p except setup
|
|
import pkg/questionable
|
|
import ./clioption
|
|
|
|
export clioption
|
|
export confutils
|
|
|
|
type
|
|
CodexConfigs* = object
|
|
configs*: seq[CodexConfig]
|
|
CodexConfig* = object
|
|
cliOptions: Table[StartUpCmd, Table[string, CliOption]]
|
|
cliPersistenceOptions: Table[PersistenceCmd, Table[string, CliOption]]
|
|
debugEnabled*: bool
|
|
CodexConfigError* = object of CatchableError
|
|
|
|
proc cliArgs*(config: CodexConfig): seq[string] {.gcsafe, raises: [CodexConfigError].}
|
|
|
|
proc raiseCodexConfigError(msg: string) {.raises: [CodexConfigError].} =
|
|
raise newException(CodexConfigError, msg)
|
|
|
|
template convertError(body) =
|
|
try:
|
|
body
|
|
except CatchableError as e:
|
|
raiseCodexConfigError e.msg
|
|
|
|
proc init*(_: type CodexConfigs, nodes = 1): CodexConfigs {.raises: [].} =
|
|
CodexConfigs(configs: newSeq[CodexConfig](nodes))
|
|
|
|
func nodes*(self: CodexConfigs): int =
|
|
self.configs.len
|
|
|
|
proc checkBounds(self: CodexConfigs, idx: int) {.raises: [CodexConfigError].} =
|
|
if idx notin 0..<self.configs.len:
|
|
raiseCodexConfigError "index must be in bounds of the number of nodes"
|
|
|
|
proc buildConfig(
|
|
config: CodexConfig,
|
|
msg: string): CodexConf {.raises: [CodexConfigError].} =
|
|
|
|
proc postFix(msg: string): string =
|
|
if msg.len > 0:
|
|
": " & msg
|
|
else: ""
|
|
|
|
try:
|
|
return CodexConf.load(cmdLine = config.cliArgs, quitOnFailure = false)
|
|
except ConfigurationError as e:
|
|
raiseCodexConfigError msg & e.msg.postFix
|
|
except Exception as e:
|
|
## TODO: remove once proper exception handling added to nim-confutils
|
|
raiseCodexConfigError msg & e.msg.postFix
|
|
|
|
proc addCliOption*(
|
|
config: var CodexConfig,
|
|
group = PersistenceCmd.noCmd,
|
|
cliOption: CliOption) {.raises: [CodexConfigError].} =
|
|
|
|
var options = config.cliPersistenceOptions.getOrDefault(group)
|
|
options[cliOption.key] = cliOption # overwrite if already exists
|
|
config.cliPersistenceOptions[group] = options
|
|
discard config.buildConfig("Invalid cli arg " & $cliOption)
|
|
|
|
proc addCliOption*(
|
|
config: var CodexConfig,
|
|
group = PersistenceCmd.noCmd,
|
|
key: string, value = "") {.raises: [CodexConfigError].} =
|
|
|
|
config.addCliOption(group, CliOption(key: key, value: value))
|
|
|
|
proc addCliOption*(
|
|
config: var CodexConfig,
|
|
group = StartUpCmd.noCmd,
|
|
cliOption: CliOption) {.raises: [CodexConfigError].} =
|
|
|
|
var options = config.cliOptions.getOrDefault(group)
|
|
options[cliOption.key] = cliOption # overwrite if already exists
|
|
config.cliOptions[group] = options
|
|
discard config.buildConfig("Invalid cli arg " & $cliOption)
|
|
|
|
proc addCliOption*(
|
|
config: var CodexConfig,
|
|
group = StartUpCmd.noCmd,
|
|
key: string, value = "") {.raises: [CodexConfigError].} =
|
|
|
|
config.addCliOption(group, CliOption(key: key, value: value))
|
|
|
|
proc addCliOption*(
|
|
config: var CodexConfig,
|
|
cliOption: CliOption) {.raises: [CodexConfigError].} =
|
|
|
|
config.addCliOption(StartUpCmd.noCmd, cliOption)
|
|
|
|
proc addCliOption*(
|
|
config: var CodexConfig,
|
|
key: string, value = "") {.raises: [CodexConfigError].} =
|
|
|
|
config.addCliOption(StartUpCmd.noCmd, CliOption(key: key, value: value))
|
|
|
|
proc cliArgs*(
|
|
config: CodexConfig): seq[string] {.gcsafe, raises: [CodexConfigError].} =
|
|
## converts CodexConfig cli options and command groups in a sequence of args
|
|
## and filters out cli options by node index if provided in the CliOption
|
|
var args: seq[string] = @[]
|
|
|
|
convertError:
|
|
for cmd in StartUpCmd:
|
|
if config.cliOptions.hasKey(cmd):
|
|
if cmd != StartUpCmd.noCmd:
|
|
args.add $cmd
|
|
var opts = config.cliOptions[cmd].values.toSeq
|
|
args = args.concat( opts.map(o => $o) )
|
|
|
|
for cmd in PersistenceCmd:
|
|
if config.cliPersistenceOptions.hasKey(cmd):
|
|
if cmd != PersistenceCmd.noCmd:
|
|
args.add $cmd
|
|
var opts = config.cliPersistenceOptions[cmd].values.toSeq
|
|
args = args.concat( opts.map(o => $o) )
|
|
|
|
return args
|
|
|
|
proc logFile*(config: CodexConfig): ?string {.raises: [CodexConfigError].} =
|
|
let built = config.buildConfig("Invalid codex config cli params")
|
|
built.logFile
|
|
|
|
proc logLevel*(config: CodexConfig): LogLevel {.raises: [CodexConfigError].} =
|
|
convertError:
|
|
let built = config.buildConfig("Invalid codex config cli params")
|
|
return parseEnum[LogLevel](built.logLevel.toUpperAscii)
|
|
|
|
proc debug*(
|
|
self: CodexConfigs,
|
|
idx: int,
|
|
enabled = true): CodexConfigs {.raises: [CodexConfigError].} =
|
|
## output log in stdout for a specific node in the group
|
|
|
|
self.checkBounds idx
|
|
|
|
var startConfig = self
|
|
startConfig.configs[idx].debugEnabled = enabled
|
|
return startConfig
|
|
|
|
proc debug*(self: CodexConfigs, enabled = true): CodexConfigs {.raises: [].} =
|
|
## output log in stdout for all nodes in group
|
|
var startConfig = self
|
|
for config in startConfig.configs.mitems:
|
|
config.debugEnabled = enabled
|
|
return startConfig
|
|
|
|
proc withLogFile*(
|
|
self: CodexConfigs,
|
|
idx: int): CodexConfigs {.raises: [CodexConfigError].} =
|
|
|
|
self.checkBounds idx
|
|
|
|
var startConfig = self
|
|
startConfig.configs[idx].addCliOption("--log-file", "<updated_in_test>")
|
|
return startConfig
|
|
|
|
proc withLogFile*(
|
|
self: CodexConfigs): CodexConfigs {.raises: [CodexConfigError].} =
|
|
## typically called from test, sets config such that a log file should be
|
|
## created
|
|
var startConfig = self
|
|
for config in startConfig.configs.mitems:
|
|
config.addCliOption("--log-file", "<updated_in_test>")
|
|
return startConfig
|
|
|
|
proc withLogFile*(
|
|
self: var CodexConfig,
|
|
logFile: string) {.raises: [CodexConfigError].} = #: CodexConfigs =
|
|
## typically called internally from the test suite, sets a log file path to
|
|
## be created during the test run, for a specified node in the group
|
|
# var config = self
|
|
self.addCliOption("--log-file", logFile)
|
|
# return startConfig
|
|
|
|
proc withLogLevel*(
|
|
self: CodexConfig,
|
|
level: LogLevel | string): CodexConfig {.raises: [CodexConfigError].} =
|
|
|
|
var config = self
|
|
config.addCliOption("--log-level", $level)
|
|
return config
|
|
|
|
proc withLogLevel*(
|
|
self: CodexConfigs,
|
|
idx: int,
|
|
level: LogLevel | string): CodexConfigs {.raises: [CodexConfigError].} =
|
|
|
|
self.checkBounds idx
|
|
|
|
var startConfig = self
|
|
startConfig.configs[idx].addCliOption("--log-level", $level)
|
|
return startConfig
|
|
|
|
proc withLogLevel*(
|
|
self: CodexConfigs,
|
|
level: LogLevel | string): CodexConfigs {.raises: [CodexConfigError].} =
|
|
|
|
var startConfig = self
|
|
for config in startConfig.configs.mitems:
|
|
config.addCliOption("--log-level", $level)
|
|
return startConfig
|
|
|
|
proc withSimulateProofFailures*(
|
|
self: CodexConfigs,
|
|
idx: int,
|
|
failEveryNProofs: int
|
|
): CodexConfigs {.raises: [CodexConfigError].} =
|
|
|
|
self.checkBounds idx
|
|
|
|
var startConfig = self
|
|
startConfig.configs[idx].addCliOption(
|
|
StartUpCmd.persistence, "--simulate-proof-failures", $failEveryNProofs)
|
|
return startConfig
|
|
|
|
proc withSimulateProofFailures*(
|
|
self: CodexConfigs,
|
|
failEveryNProofs: int): CodexConfigs {.raises: [CodexConfigError].} =
|
|
|
|
var startConfig = self
|
|
for config in startConfig.configs.mitems:
|
|
config.addCliOption(
|
|
StartUpCmd.persistence, "--simulate-proof-failures", $failEveryNProofs)
|
|
return startConfig
|
|
|
|
proc logLevelWithTopics(
|
|
config: CodexConfig,
|
|
topics: varargs[string]): string {.raises: [CodexConfigError].} =
|
|
|
|
convertError:
|
|
var logLevel = LogLevel.INFO
|
|
let built = config.buildConfig("Invalid codex config cli params")
|
|
logLevel = parseEnum[LogLevel](built.logLevel.toUpperAscii)
|
|
let level = $logLevel & ";TRACE: " & topics.join(",")
|
|
return level
|
|
|
|
proc withLogTopics*(
|
|
self: CodexConfigs,
|
|
idx: int,
|
|
topics: varargs[string]): CodexConfigs {.raises: [CodexConfigError].} =
|
|
|
|
self.checkBounds idx
|
|
|
|
convertError:
|
|
let config = self.configs[idx]
|
|
let level = config.logLevelWithTopics(topics)
|
|
var startConfig = self
|
|
return startConfig.withLogLevel(idx, level)
|
|
|
|
proc withLogTopics*(
|
|
self: CodexConfigs,
|
|
topics: varargs[string]
|
|
): CodexConfigs {.raises: [CodexConfigError].} =
|
|
|
|
var startConfig = self
|
|
for config in startConfig.configs.mitems:
|
|
let level = config.logLevelWithTopics(topics)
|
|
config = config.withLogLevel(level)
|
|
return startConfig
|
|
|
|
proc withStorageQuota*(
|
|
self: CodexConfigs,
|
|
idx: int,
|
|
quota: NBytes): CodexConfigs {.raises: [CodexConfigError].} =
|
|
|
|
self.checkBounds idx
|
|
|
|
var startConfig = self
|
|
startConfig.configs[idx].addCliOption("--storage-quota", $quota)
|
|
return startConfig
|
|
|
|
proc withStorageQuota*(
|
|
self: CodexConfigs,
|
|
quota: NBytes): CodexConfigs {.raises: [CodexConfigError].} =
|
|
|
|
var startConfig = self
|
|
for config in startConfig.configs.mitems:
|
|
config.addCliOption("--storage-quota", $quota)
|
|
return startConfig
|