v0.4.0
Some checks failed
ci/woodpecker/push/gitea Pipeline was successful
ci/woodpecker/push/go-lint Pipeline failed
ci/woodpecker/tag/gitea Pipeline failed
ci/woodpecker/tag/go-lint Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline is running
ci/woodpecker/push/publish-docs Pipeline was successful

This commit is contained in:
2023-09-08 23:42:13 -05:00
parent 6ccb75f4fa
commit a4214b2b3f
25 changed files with 522 additions and 375 deletions

View File

@ -17,12 +17,13 @@ import (
"embed"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
//go:embed templates/*.txt
var templates embed.FS
var requiredKeys = []string{"commands", "cmd-configs"}
var requiredKeys = []string{"commands"}
var Sprintf = fmt.Sprintf
@ -30,7 +31,7 @@ var Sprintf = fmt.Sprintf
// The environment of local commands will be the machine's environment plus any extra
// variables specified in the Env file or Environment.
// Dir can also be specified for local commands.
func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *ConfigOpts) ([]string, error) {
func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([]string, error) {
var (
outputArr []string
@ -52,13 +53,13 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
command.Type = strings.TrimSpace(command.Type)
if command.Type != "" {
log.Info().Str("Command", fmt.Sprintf("Running script %s on host %s", command.Cmd, *command.Host)).Send()
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running script %s on host %s", command.Cmd, *command.Host)).Send()
} else {
log.Info().Str("Command", fmt.Sprintf("Running command %s %s on host %s", command.Cmd, ArgsStr, *command.Host)).Send()
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s %s on host %s", command.Cmd, ArgsStr, *command.Host)).Send()
}
if command.RemoteHost.SshClient == nil {
err := command.RemoteHost.ConnectToSSHHost(opts, backyConf)
err := command.RemoteHost.ConnectToSSHHost(opts)
if err != nil {
return nil, err
}
@ -67,7 +68,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
// Retry connecting to host; if that fails, error. If it does not fail, try to create new session
if err != nil {
connErr := command.RemoteHost.ConnectToSSHHost(opts, backyConf)
connErr := command.RemoteHost.ConnectToSSHHost(opts)
if connErr != nil {
return nil, fmt.Errorf("error creating session: %v, and error creating new connection to host: %v", err, connErr)
}
@ -80,7 +81,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
defer commandSession.Close()
injectEnvIntoSSH(envVars, commandSession, opts, log)
injectEnvIntoSSH(envVars, commandSession, opts, cmdCtxLogger)
cmd := command.Cmd
for _, a := range command.Args {
cmd += " " + a
@ -98,7 +99,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
// did the program panic while writing to the buffer?
defer func() {
if err := recover(); err != nil {
log.Info().Msg(fmt.Sprintf("panic occured writing to buffer: %x", err))
cmdCtxLogger.Info().Msg(fmt.Sprintf("panic occured writing to buffer: %x", err))
}
}()
@ -119,7 +120,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str)
}
log.Info().Fields(outMap).Send()
cmdCtxLogger.Info().Fields(outMap).Send()
}
return outputArr, err
}
@ -211,7 +212,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str)
}
log.Info().Fields(outMap).Send()
cmdCtxLogger.Info().Fields(outMap).Send()
}
return outputArr, err
}
@ -224,7 +225,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str)
}
log.Info().Fields(outMap).Send()
cmdCtxLogger.Info().Fields(outMap).Send()
}
return outputArr, nil
}
@ -240,18 +241,18 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str)
}
log.Info().Fields(outMap).Send()
cmdCtxLogger.Info().Fields(outMap).Send()
}
if err != nil {
log.Error().Err(fmt.Errorf("error when running cmd: %s: %w", command.Cmd, err)).Send()
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd: %s: %w", command.Cmd, err)).Send()
return outputArr, err
}
} else {
var err error
if command.Shell != "" {
log.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine in %s", command.Cmd, ArgsStr, command.Shell)).Send()
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine in %s", command.Cmd, ArgsStr, command.Shell)).Send()
ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr)
@ -260,7 +261,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if command.Dir != nil {
localCMD.Dir = *command.Dir
}
injectEnvIntoLocalCMD(envVars, localCMD, log)
injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger)
cmdOutWriters = io.MultiWriter(&cmdOutBuf)
@ -286,12 +287,12 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
}
if err != nil {
log.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send()
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send()
return outputArr, err
}
return outputArr, nil
}
log.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine", command.Cmd, ArgsStr)).Send()
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine", command.Cmd, ArgsStr)).Send()
localCMD := exec.Command(command.Cmd, command.Args...)
if command.Dir != nil {
@ -299,7 +300,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
}
// fmt.Printf("%v\n", envVars.env)
injectEnvIntoLocalCMD(envVars, localCMD, log)
injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger)
cmdOutWriters = io.MultiWriter(&cmdOutBuf)
// fmt.Printf("%v\n", localCMD.Environ())
@ -318,46 +319,46 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str)
}
log.Info().Fields(outMap).Send()
cmdCtxLogger.Info().Fields(outMap).Send()
}
if err != nil {
log.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send()
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send()
return outputArr, err
}
}
return outputArr, nil
}
func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigFile, results chan<- string, opts *ConfigOpts) {
func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<- string, opts *ConfigOpts) {
for list := range jobs {
fieldsMap := make(map[string]interface{})
fieldsMap["list"] = list.Name
cmdLog := config.Logger.Info()
cmdLog := opts.Logger.Info()
var count int
var cmdsRan []string
var outStructArr []outStruct
for _, cmd := range list.Order {
currentCmd := config.Cmds[cmd].Cmd
currentCmd := opts.Cmds[cmd].Cmd
fieldsMap["cmd"] = config.Cmds[cmd].Cmd
cmdToRun := config.Cmds[cmd]
fieldsMap["cmd"] = opts.Cmds[cmd].Cmd
cmdToRun := opts.Cmds[cmd]
cmdLog.Fields(fieldsMap).Send()
cmdLogger := config.Logger.With().
cmdLogger := opts.Logger.With().
Str("backy-cmd", cmd).Str("Host", "local machine").
Logger()
if cmdToRun.Host != nil {
cmdLogger = config.Logger.With().
cmdLogger = opts.Logger.With().
Str("backy-cmd", cmd).Str("Host", *cmdToRun.Host).
Logger()
}
outputArr, runOutErr := cmdToRun.RunCmd(cmdLogger, config, opts)
outputArr, runOutErr := cmdToRun.RunCmd(cmdLogger, opts)
if list.NotifyConfig != nil {
if cmdToRun.GetOutput || list.GetOutput {
@ -380,7 +381,7 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF
errStruct["listName"] = list.Name
errStruct["Command"] = currentCmd
errStruct["Cmd"] = cmd
errStruct["Args"] = config.Cmds[cmd].Args
errStruct["Args"] = opts.Cmds[cmd].Args
errStruct["Err"] = runOutErr
errStruct["CmdsRan"] = cmdsRan
errStruct["Output"] = outputArr
@ -390,17 +391,17 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF
tmpErr := msgTemps.err.Execute(&errMsg, errStruct)
if tmpErr != nil {
config.Logger.Err(tmpErr).Send()
opts.Logger.Err(tmpErr).Send()
}
notifySendErr := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s failed on command %s ", list.Name, cmd), errMsg.String())
notifySendErr := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s failed", list.Name), errMsg.String())
if notifySendErr != nil {
config.Logger.Err(notifySendErr).Send()
opts.Logger.Err(notifySendErr).Send()
}
}
config.Logger.Err(runOutErr).Send()
opts.Logger.Err(runOutErr).Send()
break
} else {
@ -420,14 +421,14 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF
tmpErr := msgTemps.success.Execute(&successMsg, successStruct)
if tmpErr != nil {
config.Logger.Err(tmpErr).Send()
opts.Logger.Err(tmpErr).Send()
break
}
err := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s succeded", list.Name), successMsg.String())
if err != nil {
config.Logger.Err(err).Send()
opts.Logger.Err(err).Send()
}
}
} else {
@ -442,22 +443,22 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF
}
// RunListConfig runs a command list from the ConfigFile.
func (config *ConfigFile) RunListConfig(cron string, opts *ConfigOpts) {
func (opts *ConfigOpts) RunListConfig(cron string) {
mTemps := &msgTemplates{
err: template.Must(template.New("error.txt").ParseFS(templates, "templates/error.txt")),
success: template.Must(template.New("success.txt").ParseFS(templates, "templates/success.txt")),
}
configListsLen := len(config.CmdConfigLists)
configListsLen := len(opts.CmdConfigLists)
listChan := make(chan *CmdList, configListsLen)
results := make(chan string)
// This starts up list workers, initially blocked
// because there are no jobs yet.
for w := 1; w <= configListsLen; w++ {
go cmdListWorker(mTemps, listChan, config, results, opts)
go cmdListWorker(mTemps, listChan, results, opts)
}
for listName, cmdConfig := range config.CmdConfigLists {
for listName, cmdConfig := range opts.CmdConfigLists {
if cmdConfig.Name == "" {
cmdConfig.Name = listName
}
@ -475,26 +476,26 @@ func (config *ConfigFile) RunListConfig(cron string, opts *ConfigOpts) {
<-results
}
config.closeHostConnections()
opts.closeHostConnections()
}
func (config *ConfigFile) ExecuteCmds(opts *ConfigOpts) {
func (config *ConfigOpts) ExecuteCmds(opts *ConfigOpts) {
for _, cmd := range opts.executeCmds {
cmdToRun := config.Cmds[cmd]
cmdLogger := config.Logger.With().
cmdToRun := opts.Cmds[cmd]
cmdLogger := opts.Logger.With().
Str("backy-cmd", cmd).
Logger()
_, runErr := cmdToRun.RunCmd(cmdLogger, config, opts)
_, runErr := cmdToRun.RunCmd(cmdLogger, opts)
if runErr != nil {
config.Logger.Err(runErr).Send()
opts.Logger.Err(runErr).Send()
}
}
config.closeHostConnections()
opts.closeHostConnections()
}
func (c *ConfigFile) closeHostConnections() {
func (c *ConfigOpts) closeHostConnections() {
for _, host := range c.Hosts {
c.Logger.Info().Str("server", host.HostName)
if host.isProxyHost {

View File

@ -10,40 +10,58 @@ import (
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
vault "github.com/hashicorp/vault/api"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/v2"
"github.com/mattn/go-isatty"
"github.com/rs/zerolog"
"github.com/spf13/viper"
)
func (opts *ConfigOpts) InitConfig() {
if opts.viper != nil {
return
}
backyViper := viper.New()
var homeDir string
var homeDirErr error
var backyHomeConfDir string
var configFiles []string
if strings.TrimSpace(opts.ConfigFilePath) != "" {
func (opts *ConfigOpts) InitConfig() {
homeDir, homeDirErr = os.UserHomeDir()
if homeDirErr != nil {
fmt.Println(homeDirErr)
}
backyHomeConfDir = homeDir + "/.config/backy/"
configFiles = []string{"./backy.yml", "./backy.yaml", backyHomeConfDir + "backy.yml", backyHomeConfDir + "backy.yaml"}
backyKoanf := koanf.New(".")
opts.ConfigFilePath = strings.TrimSpace(opts.ConfigFilePath)
if opts.ConfigFilePath != "" {
err := testFile(opts.ConfigFilePath)
if err != nil {
logging.ExitWithMSG(fmt.Sprintf("Could not open config file %s: %v", opts.ConfigFilePath, err), 1, nil)
}
backyViper.SetConfigFile(opts.ConfigFilePath)
if err := backyKoanf.Load(file.Provider(opts.ConfigFilePath), yaml.Parser()); err != nil {
logging.ExitWithMSG(fmt.Sprintf("error loading config: %v", err), 1, &opts.Logger)
}
} else {
backyViper.SetConfigName("backy.yml") // name of config file (with extension)
backyViper.SetConfigName("backy.yaml") // name of config file (with extension)
backyViper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
backyViper.AddConfigPath(".") // optionally look for config in the working directory
backyViper.AddConfigPath("$HOME/.config/backy") // call multiple times to add many search paths
cFileFalures := 0
for _, c := range configFiles {
if err := backyKoanf.Load(file.Provider(c), yaml.Parser()); err != nil {
cFileFalures++
} else {
opts.ConfigFilePath = c
break
}
}
if cFileFalures == len(configFiles) {
logging.ExitWithMSG(fmt.Sprintf("could not find a config file. Put one in the following paths: %v", configFiles), 1, &opts.Logger)
}
}
err := backyViper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
msg := fmt.Sprintf("fatal error reading config file %s: %v", backyViper.ConfigFileUsed(), err)
logging.ExitWithMSG(msg, 1, nil)
}
opts.viper = backyViper
opts.koanf = backyKoanf
}
// ReadConfig validates and reads the config file.
func ReadConfig(opts *ConfigOpts) *ConfigFile {
func ReadConfig(opts *ConfigOpts) *ConfigOpts {
if isatty.IsTerminal(os.Stdout.Fd()) {
os.Setenv("BACKY_TERM", "enabled")
@ -53,44 +71,38 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
os.Setenv("BACKY_TERM", "disabled")
}
backyConfigFile := NewConfig()
backyViper := opts.viper
backyKoanf := opts.koanf
opts.loadEnv()
// envFileInConfigDir := fmt.Sprintf("%s/.env", path.Dir(backyViper.ConfigFileUsed()))
// load the .env file in config file directory
// _ = godotenv.Load(envFileInConfigDir)
if backyViper.GetBool(getNestedConfig("logging", "cmd-std-out")) {
if backyKoanf.Bool(getNestedConfig("logging", "cmd-std-out")) {
os.Setenv("BACKY_STDOUT", "enabled")
}
CheckConfigValues(backyViper)
CheckConfigValues(backyKoanf, opts.ConfigFilePath)
for _, c := range opts.executeCmds {
if !backyViper.IsSet(getCmdFromConfig(c)) {
logging.ExitWithMSG(Sprintf("command %s is not in config file %s", c, backyViper.ConfigFileUsed()), 1, nil)
if !backyKoanf.Exists(getCmdFromConfig(c)) {
logging.ExitWithMSG(Sprintf("command %s is not in config file %s", c, opts.ConfigFilePath), 1, nil)
}
}
for _, l := range opts.executeLists {
if !backyViper.IsSet(getCmdListFromConfig(l)) {
if !backyKoanf.Exists(getCmdListFromConfig(l)) {
logging.ExitWithMSG(Sprintf("list %s not found", l), 1, nil)
}
}
var (
// backyLoggingOpts *viper.Viper
verbose bool
logFile string
)
verbose = backyViper.GetBool(getLoggingKeyFromConfig("verbose"))
verbose = backyKoanf.Bool(getLoggingKeyFromConfig("verbose"))
logFile = fmt.Sprintf("%s/backy.log", path.Dir(backyViper.ConfigFileUsed()))
if backyViper.IsSet(getLoggingKeyFromConfig("file")) {
logFile = backyViper.GetString(getLoggingKeyFromConfig("file"))
logFile = fmt.Sprintf("%s/backy.log", path.Dir(opts.ConfigFilePath))
if backyKoanf.Exists(getLoggingKeyFromConfig("file")) {
logFile = backyKoanf.String(getLoggingKeyFromConfig("file"))
}
zerolog.SetGlobalLevel(zerolog.InfoLevel)
if verbose {
@ -99,7 +111,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
os.Setenv("BACKY_LOGLEVEL", Sprintf("%v", globalLvl))
}
consoleLoggingDisabled := backyViper.GetBool(getLoggingKeyFromConfig("console-disabled"))
consoleLoggingDisabled := backyKoanf.Bool(getLoggingKeyFromConfig("console-disabled"))
os.Setenv("BACKY_CONSOLE_LOGGING", "enabled")
// Other qualifiers can go here as well
@ -111,42 +123,30 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
log := zerolog.New(writers).With().Timestamp().Logger()
backyConfigFile.Logger = log
opts.Logger = log
log.Info().Str("config file", backyViper.ConfigFileUsed()).Send()
commandsMap := backyViper.GetStringMapString("commands")
commandsMapViper := backyViper.Sub("commands")
unmarshalErr := commandsMapViper.Unmarshal(&backyConfigFile.Cmds)
log.Info().Str("config file", opts.ConfigFilePath).Send()
unmarshalErr := backyKoanf.UnmarshalWithConf("commands", &opts.Cmds, koanf.UnmarshalConf{Tag: "yaml"})
if unmarshalErr != nil {
panic(fmt.Errorf("error unmarshalling cmds struct: %w", unmarshalErr))
}
hostConfigsMap := make(map[string]*viper.Viper)
for cmdName, cmdConf := range backyConfigFile.Cmds {
for cmdName, cmdConf := range opts.Cmds {
envFileErr := testFile(cmdConf.Env)
if envFileErr != nil {
backyConfigFile.Logger.Info().Str("cmd", cmdName).Err(envFileErr).Send()
opts.Logger.Info().Str("cmd", cmdName).Err(envFileErr).Send()
os.Exit(1)
}
expandEnvVars(opts.backyEnv, cmdConf.Environment)
host := cmdConf.Host
if host != nil {
if backyViper.IsSet(getNestedConfig("hosts", *host)) {
hostconfig := backyViper.Sub(getNestedConfig("hosts", *host))
hostConfigsMap[*host] = hostconfig
}
}
}
hostsMapViper := backyViper.Sub("hosts")
unmarshalErr = hostsMapViper.Unmarshal(&backyConfigFile.Hosts)
unmarshalErr = backyKoanf.UnmarshalWithConf("hosts", &opts.Hosts, koanf.UnmarshalConf{Tag: "yaml"})
if unmarshalErr != nil {
panic(fmt.Errorf("error unmarshalling hosts struct: %w", unmarshalErr))
}
for hostConfigName, host := range backyConfigFile.Hosts {
for hostConfigName, host := range opts.Hosts {
if host.Host == "" {
host.Host = hostConfigName
}
@ -155,7 +155,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
if len(proxyHosts) > 1 {
for hostNum, h := range proxyHosts {
if hostNum > 1 {
proxyHost, defined := backyConfigFile.Hosts[h]
proxyHost, defined := opts.Hosts[h]
if defined {
host.ProxyHost = append(host.ProxyHost, proxyHost)
} else {
@ -163,7 +163,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
host.ProxyHost = append(host.ProxyHost, newProxy)
}
} else {
proxyHost, defined := backyConfigFile.Hosts[h]
proxyHost, defined := opts.Hosts[h]
if defined {
host.ProxyHost = append(host.ProxyHost, proxyHost)
} else {
@ -173,7 +173,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
}
}
} else {
proxyHost, defined := backyConfigFile.Hosts[proxyHosts[0]]
proxyHost, defined := opts.Hosts[proxyHosts[0]]
if defined {
host.ProxyHost = append(host.ProxyHost, proxyHost)
} else {
@ -184,33 +184,29 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
}
}
cmdListCfg := backyViper.Sub("cmd-configs")
unmarshalErr = cmdListCfg.Unmarshal(&backyConfigFile.CmdConfigLists)
if unmarshalErr != nil {
panic(fmt.Errorf("error unmarshalling cmd list struct: %w", unmarshalErr))
if backyKoanf.Exists("cmd-lists") {
unmarshalErr = backyKoanf.UnmarshalWithConf("cmd-lists", &opts.CmdConfigLists, koanf.UnmarshalConf{Tag: "yaml"})
if unmarshalErr != nil {
logging.ExitWithMSG((fmt.Sprintf("error unmarshalling cmd list struct: %v", unmarshalErr)), 1, &opts.Logger)
}
}
var cmdNotFoundSliceErr []error
for cmdListName, cmdList := range backyConfigFile.CmdConfigLists {
for cmdListName, cmdList := range opts.CmdConfigLists {
if opts.useCron {
cron := strings.TrimSpace(cmdList.Cron)
if cron == "" {
delete(backyConfigFile.CmdConfigLists, cmdListName)
delete(opts.CmdConfigLists, cmdListName)
}
}
for _, cmdInList := range cmdList.Order {
_, cmdNameFound := backyConfigFile.Cmds[cmdInList]
_, cmdNameFound := opts.Cmds[cmdInList]
if !cmdNameFound {
cmdNotFoundStr := fmt.Sprintf("command %s in list %s is not defined in commands section in config file", cmdInList, cmdListName)
cmdNotFoundErr := errors.New(cmdNotFoundStr)
cmdNotFoundSliceErr = append(cmdNotFoundSliceErr, cmdNotFoundErr)
}
}
for _, notificationID := range cmdList.Notifications {
if !backyViper.IsSet(getNestedConfig("notifications", notificationID)) {
logging.ExitWithMSG(fmt.Sprintf("%s in list %s not found in notifications", notificationID, cmdListName), 1, nil)
}
}
}
if len(cmdNotFoundSliceErr) > 0 {
@ -218,39 +214,35 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
cmdNotFoundErrorLog.Errs("commands not found", cmdNotFoundSliceErr).Send()
}
if opts.useCron && (len(backyConfigFile.CmdConfigLists) == 0) {
if opts.useCron && (len(opts.CmdConfigLists) == 0) {
logging.ExitWithMSG("No cron fields detected in any command lists", 1, nil)
}
for c := range commandsMap {
for c := range opts.Cmds {
if opts.executeCmds != nil && !contains(opts.executeCmds, c) {
delete(backyConfigFile.Cmds, c)
delete(opts.Cmds, c)
}
}
if len(opts.executeLists) > 0 {
for l := range backyConfigFile.CmdConfigLists {
for l := range opts.CmdConfigLists {
if !contains(opts.executeLists, l) {
delete(backyConfigFile.CmdConfigLists, l)
delete(opts.CmdConfigLists, l)
}
}
}
if backyViper.IsSet("notifications") {
notificationsMap := backyViper.GetStringMap("notifications")
for id := range notificationsMap {
notifConfig := backyViper.Sub(getNestedConfig("notifications", id))
config := &NotificationsConfig{
Config: notifConfig,
Enabled: true,
}
backyConfigFile.Notifications[id] = config
if backyKoanf.Exists("notifications") {
unmarshalErr = backyKoanf.UnmarshalWithConf("notifications", &opts.NotificationConf, koanf.UnmarshalConf{Tag: "yaml"})
if unmarshalErr != nil {
fmt.Printf("error unmarshalling notifications object: %v", unmarshalErr)
}
}
for _, cmd := range backyConfigFile.Cmds {
for _, cmd := range opts.Cmds {
if cmd.Host != nil {
host, hostFound := backyConfigFile.Hosts[*cmd.Host]
host, hostFound := opts.Hosts[*cmd.Host]
if hostFound {
cmd.RemoteHost = host
cmd.RemoteHost.Host = host.Host
@ -258,19 +250,18 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
cmd.RemoteHost.HostName = host.HostName
}
} else {
backyConfigFile.Hosts[*cmd.Host] = &Host{Host: *cmd.Host}
opts.Hosts[*cmd.Host] = &Host{Host: *cmd.Host}
cmd.RemoteHost = &Host{Host: *cmd.Host}
}
}
}
backyConfigFile.SetupNotify()
opts.ConfigFile = backyConfigFile
opts.SetupNotify()
if err := opts.setupVault(); err != nil {
log.Err(err).Send()
}
opts.ConfigFile = backyConfigFile
return backyConfigFile
return opts
}
func getNestedConfig(nestedConfig, key string) string {
@ -289,16 +280,16 @@ func getLoggingKeyFromConfig(key string) string {
}
func getCmdListFromConfig(list string) string {
return fmt.Sprintf("cmd-configs.%s", list)
return fmt.Sprintf("cmd-lists.%s", list)
}
func (opts *ConfigOpts) setupVault() error {
if !opts.viper.GetBool("vault.enabled") {
if !opts.koanf.Bool("vault.enabled") {
return nil
}
config := vault.DefaultConfig()
config.Address = opts.viper.GetString("vault.address")
config.Address = opts.koanf.String("vault.address")
if strings.TrimSpace(config.Address) == "" {
config.Address = os.Getenv("VAULT_ADDR")
}
@ -308,7 +299,7 @@ func (opts *ConfigOpts) setupVault() error {
return err
}
token := opts.viper.GetString("vault.token")
token := opts.koanf.String("vault.token")
if strings.TrimSpace(token) == "" {
token = os.Getenv("VAULT_TOKEN")
}
@ -318,10 +309,9 @@ func (opts *ConfigOpts) setupVault() error {
client.SetToken(token)
cmdListCfg := opts.viper.Sub("viper.keys")
unmarshalErr := cmdListCfg.Unmarshal(&opts.VaultKeys)
unmarshalErr := opts.koanf.UnmarshalWithConf("vault.keys", &opts.VaultKeys, koanf.UnmarshalConf{Tag: "yaml"})
if unmarshalErr != nil {
panic(fmt.Errorf("error unmarshalling viper.keys into struct: %w", unmarshalErr))
logging.ExitWithMSG(fmt.Sprintf("error unmarshalling vault.keys into struct: %v", unmarshalErr), 1, &opts.Logger)
}
opts.vaultClient = client

View File

@ -5,31 +5,34 @@
package backy
import (
"fmt"
"strings"
"time"
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
"github.com/go-co-op/gocron"
)
func (opts *ConfigOpts) Cron() {
s := gocron.NewScheduler(time.Local)
s.TagsUnique()
cmdLists := opts.ConfigFile.CmdConfigLists
cmdLists := opts.CmdConfigLists
for listName, config := range cmdLists {
if config.Name == "" {
config.Name = listName
}
cron := strings.TrimSpace(config.Cron)
if cron != "" {
opts.ConfigFile.Logger.Info().Str("Scheduling cron list", config.Name).Str("Time", cron).Send()
opts.Logger.Info().Str("Scheduling cron list", config.Name).Str("Time", cron).Send()
_, err := s.CronWithSeconds(cron).Tag(config.Name).Do(func(cron string) {
opts.ConfigFile.RunListConfig(cron, opts)
opts.RunListConfig(cron)
}, cron)
if err != nil {
panic(err)
logging.ExitWithMSG(fmt.Sprintf("error: %v", err), 1, &opts.Logger)
}
}
}
opts.ConfigFile.Logger.Info().Msg("Starting cron mode...")
opts.Logger.Info().Msg("Starting cron mode...")
s.StartBlocking()
}

5
pkg/backy/list.go Normal file
View File

@ -0,0 +1,5 @@
package backy
func (opts *ConfigOpts) ListConfiguration() {
}

View File

@ -18,7 +18,7 @@ const mongoConfigKey = "global.mongo"
func (opts *ConfigOpts) InitMongo() {
if !opts.viper.GetBool(getMongoConfigKey("enabled")) {
if !opts.koanf.Bool(getMongoConfigKey("enabled")) {
return
}
var (
@ -27,37 +27,37 @@ func (opts *ConfigOpts) InitMongo() {
)
// TODO: Get uri and creditials from config
host := opts.viper.GetString(getMongoConfigKey("host"))
port := opts.viper.GetInt32(getMongoConfigKey("port"))
host := opts.koanf.String(getMongoConfigKey("host"))
port := opts.koanf.Int64(getMongoConfigKey("port"))
ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer ctxCancel()
client, err = mongo.Connect(ctx, options.Client().ApplyURI(fmt.Sprintf("mongo://%s:%d", host, port)))
if opts.viper.GetBool(getMongoConfigKey("prod")) {
mongoEnvFileSet := opts.viper.IsSet(getMongoConfigKey("env"))
if opts.koanf.Bool(getMongoConfigKey("prod")) {
mongoEnvFileSet := opts.koanf.Exists(getMongoConfigKey("env"))
if mongoEnvFileSet {
getMongoConfigFromEnv(opts)
}
auth := options.Credential{}
auth.Password = opts.viper.GetString("global.mongo.password")
auth.Username = opts.viper.GetString("global.mongo.username")
auth.Password = opts.koanf.String("global.mongo.password")
auth.Username = opts.koanf.String("global.mongo.username")
client, err = mongo.Connect(ctx, options.Client().SetAuth(auth).ApplyURI("mongodb://localhost:27017"))
}
if err != nil {
opts.ConfigFile.Logger.Fatal().Err(err).Send()
opts.Logger.Fatal().Err(err).Send()
}
if err != nil {
opts.ConfigFile.Logger.Fatal().Err(err).Send()
opts.Logger.Fatal().Err(err).Send()
}
defer client.Disconnect(ctx)
err = client.Ping(ctx, readpref.Primary())
if err != nil {
opts.ConfigFile.Logger.Fatal().Err(err).Send()
opts.Logger.Fatal().Err(err).Send()
}
databases, err := client.ListDatabaseNames(ctx, bson.M{})
if err != nil {
opts.ConfigFile.Logger.Fatal().Err(err).Send()
opts.Logger.Fatal().Err(err).Send()
}
fmt.Println(databases)
backyDB := client.Database("backy")
@ -68,7 +68,7 @@ func (opts *ConfigOpts) InitMongo() {
}
func getMongoConfigFromEnv(opts *ConfigOpts) error {
mongoEnvFile, err := os.Open(opts.viper.GetString("global.mongo.env"))
mongoEnvFile, err := os.Open(opts.koanf.String("global.mongo.env"))
if err != nil {
return err
}
@ -84,8 +84,8 @@ func getMongoConfigFromEnv(opts *ConfigOpts) error {
if !mongoUserFound {
return errors.New("MONGO_PASSWORD not set in " + mongoEnvFile.Name())
}
opts.viper.Set(mongoConfigKey+".password", mongoPW)
opts.viper.Set(mongoConfigKey+".username", mongoUser)
opts.koanf.Set(mongoConfigKey+".password", mongoPW)
opts.koanf.Set(mongoConfigKey+".username", mongoUser)
return nil
}

View File

@ -5,67 +5,76 @@ package backy
import (
"fmt"
"strings"
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/mail"
"github.com/nikoksr/notify/service/matrix"
"maunium.net/go/mautrix/id"
)
type matrixStruct struct {
homeserver string
roomid id.RoomID
accessToken string
userId id.UserID
type MatrixStruct struct {
Homeserver string `yaml:"homeserver"`
Roomid id.RoomID `yaml:"room-id"`
AccessToken string `yaml:"access-token"`
UserId id.UserID `yaml:"user-id"`
}
type mailConfig struct {
senderaddress string
host string
to []string
username string
password string
port string
}
func SetupCommandsNotifiers(backyConfig ConfigFile, ids ...string) {
type MailConfig struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
Username string `yaml:"username"`
SenderAddress string `yaml:"senderaddress"`
To []string `yaml:"to"`
Password string `yaml:"password"`
}
// SetupNotify sets up notify instances for each command list.
func (opts *ConfigOpts) SetupNotify() {
func (backyConfig *ConfigFile) SetupNotify() {
// check if we have individual commands instead of lists to execute
if len(opts.executeCmds) != 0 {
return
}
for _, cmdConfig := range backyConfig.CmdConfigLists {
for confName, cmdConfig := range opts.CmdConfigLists {
var services []notify.Notifier
for notifyID := range backyConfig.Notifications {
if contains(cmdConfig.Notifications, notifyID) {
for _, id := range cmdConfig.Notifications {
if !strings.Contains(id, ".") {
opts.Logger.Info().Str("id", id).Str("list", cmdConfig.Name).Msg("key does not contain a \".\" Make sure to follow the docs: https://backy.cybershell.xyz/config/notifications/")
logging.ExitWithMSG(fmt.Sprintf("notification id %s in cmd list %s does not contain a \".\" \nMake sure to follow the docs: https://backy.cybershell.xyz/config/notifications/", id, cmdConfig.Name), 1, &opts.Logger)
}
if backyConfig.Notifications[notifyID].Enabled {
config := backyConfig.Notifications[notifyID].Config
switch config.GetString("type") {
case "matrix":
mtrx := matrixStruct{
userId: id.UserID(config.GetString("user-id")),
roomid: id.RoomID(config.GetString("room-id")),
accessToken: config.GetString("access-token"),
homeserver: config.GetString("homeserver"),
}
mtrxClient, _ := setupMatrix(mtrx)
services = append(services, mtrxClient)
case "mail":
mailCfg := mailConfig{
senderaddress: config.GetString("senderaddress"),
password: config.GetString("password"),
username: config.GetString("username"),
to: config.GetStringSlice("to"),
host: config.GetString("host"),
port: fmt.Sprint(config.GetUint16("port")),
}
mailClient := setupMail(mailCfg)
services = append(services, mailClient)
}
confSplit := strings.Split(id, ".")
confType := confSplit[0]
confId := confSplit[1]
switch confType {
case "mail":
conf, ok := opts.NotificationConf.MailConfig[confId]
if !ok {
opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in mail object", confId)).Str("list", confName).Send()
continue
}
mailConf := setupMail(conf)
services = append(services, mailConf)
case "matrix":
conf, ok := opts.NotificationConf.MatrixConfig[confId]
if !ok {
opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in matrix object", confId)).Str("list", confName).Send()
continue
}
mtrxConf, mtrxErr := setupMatrix(conf)
if mtrxErr != nil {
opts.Logger.Info().Str("list", confName).Err(fmt.Errorf("error: configuring matrix id %s failed during setup: %w", id, mtrxErr))
continue
}
// append the services
services = append(services, mtrxConf)
// service is not recognized
default:
opts.Logger.Info().Err(fmt.Errorf("id %s not found", id)).Str("list", confName).Send()
}
}
cmdConfig.NotifyConfig = notify.NewWithServices(services...)
@ -74,19 +83,18 @@ func (backyConfig *ConfigFile) SetupNotify() {
// logging.ExitWithMSG("This was a test of notifications", 0, nil)
}
func setupMatrix(config matrixStruct) (*matrix.Matrix, error) {
matrixClient, matrixErr := matrix.New(config.userId, config.roomid, config.homeserver, config.accessToken)
func setupMatrix(config MatrixStruct) (*matrix.Matrix, error) {
matrixClient, matrixErr := matrix.New(config.UserId, config.Roomid, config.Homeserver, config.AccessToken)
if matrixErr != nil {
panic(matrixErr)
return nil, matrixErr
}
return matrixClient, nil
}
func setupMail(config mailConfig) *mail.Mail {
mailClient := mail.New(config.senderaddress, config.host+":"+config.port)
mailClient.AuthenticateSMTP("", config.username, config.password, config.host)
mailClient.AddReceivers(config.to...)
func setupMail(config MailConfig) *mail.Mail {
mailClient := mail.New(config.SenderAddress, config.Host+":"+config.Port)
mailClient.AuthenticateSMTP("", config.Username, config.Password, config.Host)
mailClient.AddReceivers(config.To...)
mailClient.BodyFormat(mail.PlainText)
return mailClient
}

View File

@ -28,7 +28,7 @@ var TS = strings.TrimSpace
// It returns an ssh.Client used to run commands against.
// If configFile is empty, any required configuration is looked up in the default config files
// If any value is not found, defaults are used
func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile) error {
func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts) error {
// var sshClient *ssh.Client
var connectErr error
@ -47,6 +47,11 @@ func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile)
var configFile *os.File
var sshConfigFileOpenErr error
if !remoteConfig.useDefaultConfig {
var err error
remoteConfig.ConfigFilePath, err = resolveDir(remoteConfig.ConfigFilePath)
if err != nil {
return err
}
configFile, sshConfigFileOpenErr = os.Open(remoteConfig.ConfigFilePath)
if sshConfigFileOpenErr != nil {
return sshConfigFileOpenErr
@ -66,14 +71,14 @@ func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile)
return decodeErr
}
err := remoteConfig.GetProxyJumpFromConfig(config.Hosts)
err := remoteConfig.GetProxyJumpFromConfig(opts.Hosts)
if err != nil {
return err
}
if remoteConfig.ProxyHost != nil {
for _, proxyHost := range remoteConfig.ProxyHost {
err := proxyHost.GetProxyJumpConfig(config.Hosts, opts)
opts.ConfigFile.Logger.Info().Msgf("Proxy host: %s", proxyHost.Host)
err := proxyHost.GetProxyJumpConfig(opts.Hosts, opts)
opts.Logger.Info().Msgf("Proxy host: %s", proxyHost.Host)
if err != nil {
return err
}
@ -101,24 +106,24 @@ func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile)
return errors.Wrap(err, "could not create hostkeycallback function")
}
remoteConfig.ClientConfig.HostKeyCallback = hostKeyCallback
opts.ConfigFile.Logger.Info().Str("user", remoteConfig.ClientConfig.User).Send()
opts.Logger.Info().Str("user", remoteConfig.ClientConfig.User).Send()
remoteConfig.SshClient, connectErr = remoteConfig.ConnectThroughBastion(opts.ConfigFile.Logger)
remoteConfig.SshClient, connectErr = remoteConfig.ConnectThroughBastion(opts.Logger)
if connectErr != nil {
return connectErr
}
if remoteConfig.SshClient != nil {
config.Hosts[remoteConfig.Host] = remoteConfig
opts.Hosts[remoteConfig.Host] = remoteConfig
return nil
}
opts.ConfigFile.Logger.Info().Msgf("Connecting to host %s", remoteConfig.HostName)
opts.Logger.Info().Msgf("Connecting to host %s", remoteConfig.HostName)
remoteConfig.SshClient, connectErr = ssh.Dial("tcp", remoteConfig.HostName, remoteConfig.ClientConfig)
if connectErr != nil {
return connectErr
}
config.Hosts[remoteConfig.Host] = remoteConfig
opts.Hosts[remoteConfig.Host] = remoteConfig
return nil
}
@ -148,7 +153,7 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error {
if err != nil {
return err
}
remoteHost.PrivateKeyPassword, err = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts, opts.ConfigFile.Logger)
remoteHost.PrivateKeyPassword, err = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts, opts.Logger)
if err != nil {
return err
}
@ -167,7 +172,7 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error {
}
}
if remoteHost.Password == "" {
remoteHost.Password, err = GetPassword(remoteHost.Password, opts, opts.ConfigFile.Logger)
remoteHost.Password, err = GetPassword(remoteHost.Password, opts, opts.Logger)
if err != nil {
return err
}
@ -297,7 +302,7 @@ func GetPrivateKeyPassword(key string, opts *ConfigOpts, log zerolog.Logger) (st
} else {
prKeyPassword = key
}
prKeyPassword = GetVaultKey(prKeyPassword, opts, opts.ConfigFile.Logger)
prKeyPassword = GetVaultKey(prKeyPassword, opts, opts.Logger)
return prKeyPassword, nil
}
@ -328,7 +333,7 @@ func GetPassword(pass string, opts *ConfigOpts, log zerolog.Logger) (string, err
} else {
password = pass
}
password = GetVaultKey(password, opts, opts.ConfigFile.Logger)
password = GetVaultKey(password, opts, opts.Logger)
return password, nil
}

View File

@ -6,9 +6,9 @@ import (
vaultapi "github.com/hashicorp/vault/api"
"github.com/kevinburke/ssh_config"
"github.com/knadh/koanf/v2"
"github.com/nikoksr/notify"
"github.com/rs/zerolog"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"golang.org/x/crypto/ssh"
@ -37,7 +37,7 @@ type (
// Host defines a host to which to connect.
// If not provided, the values will be looked up in the default ssh config files
Host struct {
ConfigFilePath string `yaml:"configfilepath,omitempty"`
ConfigFilePath string `yaml:"config,omitempty"`
Host string `yaml:"host,omitempty"`
HostName string `yaml:"hostname,omitempty"`
KnownHostsFile string `yaml:"knownhostsfile,omitempty"`
@ -113,35 +113,26 @@ type (
Notifications []string `yaml:"notifications,omitempty"`
GetOutput bool `yaml:"getOutput,omitempty"`
NotifyConfig *notify.Notify
// NotificationsConfig map[string]*NotificationsConfig
// NotifyConfig map[string]*notify.Notify
}
ConfigFile struct {
ConfigOpts struct {
// Cmds holds the commands for a list.
// Key is the name of the command,
Cmds map[string]*Command `yaml:"commands"`
// CmdConfigLists holds the lists of commands to be run in order.
// Key is the command list name.
CmdConfigLists map[string]*CmdList `yaml:"cmd-configs"`
CmdConfigLists map[string]*CmdList `yaml:"cmd-lists"`
// Hosts holds the Host config.
// key is the host.
Hosts map[string]*Host `yaml:"hosts"`
// Notifications holds the config for different notifications.
Notifications map[string]*NotificationsConfig
Logger zerolog.Logger
}
ConfigOpts struct {
// Global log level
BackyLogLvl *string
// Holds config file
ConfigFile *ConfigFile
// Holds config file
ConfigFilePath string
@ -160,9 +151,13 @@ type (
vaultClient *vaultapi.Client
List ListConfig
VaultKeys []*VaultKey `yaml:"keys"`
viper *viper.Viper
koanf *koanf.Koanf
NotificationConf *Notifications `yaml:"notifications"`
}
outStruct struct {
@ -185,9 +180,9 @@ type (
Keys []*VaultKey `yaml:"keys"`
}
NotificationsConfig struct {
Config *viper.Viper
Enabled bool
Notifications struct {
MailConfig map[string]MailConfig `yaml:"mail,omitempty"`
MatrixConfig map[string]MatrixStruct `yaml:"matrix,omitempty"`
}
CmdOutput struct {
@ -204,4 +199,10 @@ type (
success *template.Template
err *template.Template
}
ListConfig struct {
Lists []string
Commands []string
Hosts []string
}
)

View File

@ -15,12 +15,65 @@ import (
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
"github.com/joho/godotenv"
"github.com/knadh/koanf/v2"
"github.com/rs/zerolog"
"github.com/spf13/viper"
"golang.org/x/crypto/ssh"
"mvdan.cc/sh/v3/shell"
)
func (c *ConfigOpts) LogLvl(level string) BackyOptionFunc {
return func(bco *ConfigOpts) {
c.BackyLogLvl = &level
}
}
// AddCommands adds commands to ConfigOpts
func AddCommands(commands []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.executeCmds = append(bco.executeCmds, commands...)
}
}
// AddCommandLists adds lists to ConfigOpts
func AddCommandLists(lists []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.executeLists = append(bco.executeLists, lists...)
}
}
// AddPrintLists adds lists to print out
func SetListsToSearch(lists []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.List.Lists = append(bco.List.Lists, lists...)
}
}
// AddPrintLists adds lists to print out
func SetCmdsToSearch(cmds []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.List.Commands = append(bco.List.Commands, cmds...)
}
}
// UseCron enables the execution of command lists at specified times
func UseCron() BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.useCron = true
}
}
func NewOpts(configFilePath string, opts ...BackyOptionFunc) *ConfigOpts {
b := &ConfigOpts{}
b.ConfigFilePath = configFilePath
for _, opt := range opts {
if opt != nil {
opt(b)
}
}
return b
}
func injectEnvIntoSSH(envVarsToInject environmentVars, process *ssh.Session, opts *ConfigOpts, log zerolog.Logger) {
if envVarsToInject.file != "" {
envPath, envPathErr := resolveDir(envVarsToInject.file)
@ -94,12 +147,12 @@ func contains(s []string, e string) bool {
return false
}
func CheckConfigValues(config *viper.Viper) {
func CheckConfigValues(config *koanf.Koanf, file string) {
for _, key := range requiredKeys {
isKeySet := config.IsSet(key)
isKeySet := config.Exists(key)
if !isKeySet {
logging.ExitWithMSG(Sprintf("Config key %s is not defined in %s. Please make sure this value is set and has the appropriate keys set.", key, config.ConfigFileUsed()), 1, nil)
logging.ExitWithMSG(Sprintf("Config key %s is not defined in %s. Please make sure this value is set and has the appropriate keys set.", key, file), 1, nil)
}
}
}
@ -116,64 +169,6 @@ func testFile(c string) error {
return nil
}
func (c *ConfigOpts) LogLvl(level string) BackyOptionFunc {
return func(bco *ConfigOpts) {
c.BackyLogLvl = &level
}
}
// AddCommands adds commands to ConfigOpts
func AddCommands(commands []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.executeCmds = append(bco.executeCmds, commands...)
}
}
// AddCommandLists adds lists to ConfigOpts
func AddCommandLists(lists []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.executeLists = append(bco.executeLists, lists...)
}
}
// UseCron enables the execution of command lists at specified times
func UseCron() BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.useCron = true
}
}
// UseCron enables the execution of command lists at specified times
func (c *ConfigOpts) AddViper(v *viper.Viper) BackyOptionFunc {
return func(bco *ConfigOpts) {
c.viper = v
}
}
func NewOpts(configFilePath string, opts ...BackyOptionFunc) *ConfigOpts {
b := &ConfigOpts{}
b.ConfigFilePath = configFilePath
for _, opt := range opts {
if opt != nil {
opt(b)
}
}
return b
}
/*
NewConfig initializes new config that holds information from the config file
*/
func NewConfig() *ConfigFile {
return &ConfigFile{
Cmds: make(map[string]*Command),
CmdConfigLists: make(map[string]*CmdList),
Hosts: make(map[string]*Host),
Notifications: make(map[string]*NotificationsConfig),
}
}
func IsTerminalActive() bool {
return os.Getenv("BACKY_TERM") == "enabled"
}
@ -203,7 +198,7 @@ func resolveDir(path string) (string, error) {
}
func (opts *ConfigOpts) loadEnv() {
envFileInConfigDir := fmt.Sprintf("%s/.env", path.Dir(opts.viper.ConfigFileUsed()))
envFileInConfigDir := fmt.Sprintf("%s/.env", path.Dir(opts.ConfigFilePath))
var backyEnv map[string]string
backyEnv, envFileErr := godotenv.Read(envFileInConfigDir)
if envFileErr != nil {