diff --git a/go.mod b/go.mod index 672db63..6565f59 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,7 @@ module github.com/TheCacophonyProject/go-utils go 1.22.3 + +require github.com/sirupsen/logrus v1.9.3 + +require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..21f9bfb --- /dev/null +++ b/go.sum @@ -0,0 +1,15 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/logging/logging.go b/logging/logging.go new file mode 100644 index 0000000..8d4357c --- /dev/null +++ b/logging/logging.go @@ -0,0 +1,39 @@ +package logging + +import ( + "fmt" + "strings" + + "github.com/sirupsen/logrus" +) + +// LogArgs holds the common logging arguments +type LogArgs struct { + LogLevel string `arg:"-l, --log-level" default:"info" help:"Set the logging level (debug, info, warn, error)"` +} + +// NewLogger returns a new logger with the given log level (info, debug, warn, error) +func NewLogger(logLevel string) *logrus.Logger { + log := logrus.New() + log.Formatter = new(customFormatter) + switch logLevel { + case "debug": + log.SetLevel(logrus.DebugLevel) + case "info": + log.SetLevel(logrus.InfoLevel) + case "warn": + log.SetLevel(logrus.WarnLevel) + case "error": + log.SetLevel(logrus.ErrorLevel) + default: + log.SetLevel(logrus.InfoLevel) + log.Warnf("Unknown log level '%s', defaulting to info", logLevel) + } + return log +} + +type customFormatter struct{} + +func (f *customFormatter) Format(entry *logrus.Entry) ([]byte, error) { + return []byte(fmt.Sprintf("[%s] %s\n", strings.ToUpper(entry.Level.String()), entry.Message)), nil +} diff --git a/saltutil/saltutil.go b/saltutil/saltutil.go new file mode 100644 index 0000000..cfb8429 --- /dev/null +++ b/saltutil/saltutil.go @@ -0,0 +1,85 @@ +package saltutil + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/TheCacophonyProject/go-utils/logging" + "github.com/sirupsen/logrus" +) + +const ( + grainsFile = "/etc/salt/grains" + nodegroupFile = "/etc/cacophony/salt-nodegroup" + minionIdFile = "/etc/salt/minion_id" +) + +// GetSaltGrains returns the salt grains. Optional to pass a logger, pass nil to use default. +func GetSaltGrains(log *logrus.Logger) (map[string]string, error) { + // Setup logger if none is provided + if log == nil { + log = logging.NewLogger("info") + } + + // Open up grains file + grains := make(map[string]string) + file, err := os.Open(grainsFile) + if os.IsNotExist(err) { + // Some devices will not have any grains set so this is not an error + log.Debugf("No grains file found: %v", err) + return grains, nil + } else if err != nil { + log.Errorf("Failed to open grains file: %v", err) + return nil, err + } + defer file.Close() + + // Parse the grains file + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + line = strings.TrimSpace(line) + parts := strings.SplitN(line, ":", 2) + if len(parts) == 2 { + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + grains[key] = value + } else if line != "" { + err = fmt.Errorf("failed to parse line in grains file: '%s'", line) + log.Errorf(err.Error()) + return nil, err + } + } + if err := scanner.Err(); err != nil { + log.Errorf("Error reading grains file: %v", err) + return nil, err + } + + return grains, nil +} + +func GetNodegroupFromFile() (string, error) { + nodegroup, err := os.ReadFile(nodegroupFile) + if err != nil { + return "", err + } + return strings.TrimSpace(string(nodegroup)), nil +} +func GetMinionID(log *logrus.Logger) (string, error) { + // Setup logger if none is provided + if log == nil { + log = logging.NewLogger("info") + } + + // Read salt minion ID + idRaw, err := os.ReadFile(minionIdFile) + if err != nil { + log.Error("Error reading minion ID: " + err.Error()) + return "", err + } + id := strings.TrimSpace(string(idRaw)) + log.Debugf("Minion ID: '%s'", id) + return id, nil +}