diff --git a/README.md b/README.md index ad4052b..4a767fe 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,17 @@ Inspired by [NETWAYS/icinga2-diagnostics](https://github.com/Icinga/icinga2-diag ## Usage +`$ support-collector` + +The CLI wizard will guide you through the possible arguments after calling the command. +If you prefer to pass the arguments in the command call, use `--non-interactive` and pass the arguments as described in the documentation. + +`--hide`, `--command-timeout` can only be used as CLI argument. + +You can also combine your CLI arguments and the wizard. All arguments you pass from the CLI will be given into the wizard. + +--- + > **WARNING:** Some passwords or secrets are automatically removed, but this no guarantee, so be careful what you share! The `--hide` flag can be used multiple times to hide sensitive data, it supports regular expressions. @@ -26,23 +37,25 @@ By default, we collect all we can find. You can control this by only enabling ce If you want to see what is collected, add `--verbose` -To collect advanced data for module `Icinga 2`, you can use the Icinga 2 API to collect data from all endpoints provided. -The API requests are performed with a global API user you have to create yourself. Just create that user in a global zone like 'director-global' - - -| Short | Long | Description | -|:-----:|:------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| -| -o | --output | Output file for the zip content (default: current directory and named like '$HOSTNAME'-netways-support-$TIMESTAMP.zip) | -| | --nodetails | Disable detailed collection including logs and more | -| | --enable | List of enabled modules (default: all) | -| | --disable | List of disabled modules (default: none) | -| | --hide | List of keywords to obfuscate. Can be used multiple times | -| | --command-timeout | Timeout for command execution in modules (default: 1m0s) | -| | --icinga2-api-user | Username of global Icinga 2 API user to collect data about Icinga 2 Infrastructure | -| | --icinga2-api-pass | Password for global Icinga 2 API user to collect data about Icinga 2 Infrastructure | -| | --icinga2-api-endpoints | List of Icinga 2 API Endpoints (including port) to collect data from. FQDN or IP address must be reachable. (Example: i2-master01.local:5665) | -| -v | --verbose | Enable verbose logging | -| -V | --version | Print version and exit | +To collect advanced data for module `Icinga 2`, you can use the Icinga 2 API to collect data from all endpoints +provided. +The API requests are performed with a global API user you have to create yourself. Just create that user in a global +zone like 'director-global' to sync it to all endpoints + +| Short | Long | Description | +|:-----:|:------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| -o | --output | Output file for the zip content (default: current directory and named like '\$HOSTNAME'-netways-support-\$TIMESTAMP.zip) | +| | --non-interactive | Disable the interactive CLI wizard | +| | --no-details | Disable detailed collection including logs and more | +| | --enable | List of enabled modules (default: all) | +| | --disable | List of disabled modules (default: none) | +| | --hide | List of keywords to obfuscate. Can be used multiple times | +| | --command-timeout | Timeout for command execution in modules (default: 1m0s) | +| | --icinga2-api-user | Username of global Icinga 2 API user to collect data about Icinga 2 Infrastructure (Optional and only for module `icinga2`) | +| | --icinga2-api-pass | Password for global Icinga 2 API user to collect data about Icinga 2 Infrastructure (Optional and only for module `icinga2`) | +| | --icinga2-api-endpoints | List of Icinga 2 API Endpoints (including port) to collect data from. FQDN or IP address must be reachable. (Example: i2-master01.local:5665) (Optional and only for module `icinga2`) | +| -v | --verbose | Enable verbose logging | +| -V | --version | Print version and exit | ## Modules diff --git a/go.mod b/go.mod index 6c46b66..778016a 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,5 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/mod v0.16.0 // indirect golang.org/x/sys v0.18.0 // indirect - golang.org/x/tools v0.19.0 // indirect ) diff --git a/go.sum b/go.sum index 415b252..cf0d2e3 100644 --- a/go.sum +++ b/go.sum @@ -19,15 +19,10 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/arguments/arguments.go b/internal/arguments/arguments.go new file mode 100644 index 0000000..8e6408c --- /dev/null +++ b/internal/arguments/arguments.go @@ -0,0 +1,167 @@ +package arguments + +import ( + "bufio" + "fmt" + flag "github.com/spf13/pflag" + "os" + "strings" +) + +var ( + NonInteractive bool + validBooleanInputs = map[string]bool{ + "y": true, + "yes": true, + "n": false, + "no": false, + "true": true, + "false": false, + } +) + +const interactiveHelpText = `Welcome to the support-collector argument wizard! +We will guide you through all required details. + +Available modules are: %s` + +type Argument struct { + Name string + InputFunction func() + Dependency func() bool +} + +type Handler struct { + scanner *bufio.Scanner + arguments []Argument +} + +// New creates a new Handler object +func New() Handler { + return Handler{ + scanner: bufio.NewScanner(os.Stdin), + } +} + +func (args *Handler) CollectArgsFromStdin(availableModules string) []error { + fmt.Printf(interactiveHelpText+"\n\n", availableModules) + + errors := make([]error, 0, len(args.arguments)) + + for _, argument := range args.arguments { + if argument.Dependency == nil { + argument.InputFunction() + continue + } + + if ok := argument.Dependency(); ok { + argument.InputFunction() + continue + } + + errors = append(errors, fmt.Errorf("argument '%s' is not matching the needed depenency. Skipping... ", argument.Name)) + } + + fmt.Print("\nInteractive wizard finished. Starting...\n\n") + + return errors +} + +func (args *Handler) NewPromptStringVar(callback *string, name, defaultValue, usage string, required bool, dependency func() bool) { + flag.StringVar(callback, name, defaultValue, usage) + + args.arguments = append(args.arguments, Argument{ + Name: name, + InputFunction: func() { + if *callback != "" { + defaultValue = *callback + } + + args.newStringPrompt(callback, defaultValue, usage, required) + }, + Dependency: dependency, + }) +} + +func (args *Handler) NewPromptStringSliceVar(callback *[]string, name string, defaultValue []string, usage string, required bool, dependency func() bool) { + flag.StringSliceVar(callback, name, defaultValue, usage) + + args.arguments = append(args.arguments, Argument{ + Name: name, + InputFunction: func() { + if len(*callback) > 0 { + defaultValue = *callback + } + + var input string + + args.newStringPrompt(&input, strings.Join(defaultValue, ","), usage, required) + *callback = strings.Split(input, ",") + }, + Dependency: dependency, + }) +} + +func (args *Handler) NewPromptBoolVar(callback *bool, name string, defaultValue bool, usage string, dependency func() bool) { + flag.BoolVar(callback, name, defaultValue, usage) + + args.arguments = append(args.arguments, Argument{ + Name: name, + InputFunction: func() { + args.newBoolPrompt(callback, defaultValue, usage) + }, + Dependency: dependency, + }) +} + +func (args *Handler) newStringPrompt(callback *string, defaultValue, usage string, required bool) { + for { + fmt.Printf("%s - (Preselection: '%s'): ", usage, defaultValue) + + if args.scanner.Scan() { + input := args.scanner.Text() + + switch { + case input != "": + *callback = input + return + case input == "" && defaultValue != "": + *callback = defaultValue + return + case input == "" && !required: + return + } + } else { + if err := args.scanner.Err(); err != nil { + _, _ = fmt.Fprintln(os.Stderr, "reading standard input:", err) + return + } + } + } +} + +func (args *Handler) newBoolPrompt(callback *bool, defaultValue bool, usage string) { + for { + fmt.Printf("%s [y/n] - (Preselection: '%t'): ", usage, defaultValue) + + if args.scanner.Scan() { + input := strings.ToLower(args.scanner.Text()) + + if input != "" && isValidBoolString(input) { + *callback = validBooleanInputs[input] + break + } else if input == "" { + *callback = defaultValue + break + } + } + } +} + +func isValidBoolString(input string) bool { + if _, ok := validBooleanInputs[input]; !ok { + return false + } + + return true +} diff --git a/internal/util/module.go b/internal/util/module.go new file mode 100644 index 0000000..0d23589 --- /dev/null +++ b/internal/util/module.go @@ -0,0 +1,15 @@ +package util + +import "os" + +// ModuleExists checks if module is installed based on given relevant paths +func ModuleExists(paths []string) bool { + for _, path := range paths { + _, err := os.Stat(path) + if err == nil { + return true + } + } + + return false +} diff --git a/main.go b/main.go index 66d9bdf..1cbd6a1 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,9 @@ package main import ( "encoding/json" "fmt" + "github.com/NETWAYS/support-collector/internal/arguments" "github.com/NETWAYS/support-collector/internal/metrics" + flag "github.com/spf13/pflag" "os" "path/filepath" "strings" @@ -35,16 +37,15 @@ import ( "github.com/mattn/go-colorable" "github.com/sirupsen/logrus" - flag "github.com/spf13/pflag" ) -const Product = "NETWAYS support collector" +const Product = "NETWAYS support-collector" // FilePrefix for the outfile file. -const FilePrefix = "netways-support" +const FilePrefix = "support-collector" const Readme = ` -The support collector allows our customers to collect relevant information from +The support-collector allows our customers to collect relevant information from their servers. A resulting ZIP file can then be provided to our support team for further inspection. @@ -107,26 +108,100 @@ var ( ) var ( - verbose, printVersion bool - enabledModules, disabledModules []string - extraObfuscators []string - outputFile string - commandTimeout = 60 * time.Second - noDetailedCollection bool - startTime = time.Now() - metric *metrics.Metrics + verbose, printVersion, noDetailedCollection bool + enabledModules, disabledModules, extraObfuscators []string + outputFile string + commandTimeout = 60 * time.Second + startTime = time.Now() + metric *metrics.Metrics + initErrors []error ) -func main() { - handleArguments() - +func init() { // Set locale to C, to avoid translations in command output _ = os.Setenv("LANG", "C") + args := arguments.New() + + // General arguments without interactive prompt + flag.StringArrayVar(&extraObfuscators, "hide", []string{}, "List of additional strings to obfuscate. Can be used multiple times and supports regex.") //nolint:lll + flag.BoolVar(&arguments.NonInteractive, "non-interactive", false, "Enable non-interactive mode") + flag.BoolVar(&printVersion, "version", false, "Print version and exit") + flag.BoolVar(&verbose, "verbose", false, "Enable verbose logging") + flag.DurationVar(&commandTimeout, "command-timeout", commandTimeout, "Timeout for command execution in modules") + + // Run specific arguments + args.NewPromptStringVar(&outputFile, "output", buildFileName(), "Filename for resulting zip", true, nil) + args.NewPromptStringSliceVar(&enabledModules, "enable", moduleOrder, "Enabled modules for collection (comma separated)", false, nil) + args.NewPromptStringSliceVar(&disabledModules, "disable", []string{}, "Explicit disabled modules for collection (comma separated)", false, nil) + args.NewPromptBoolVar(&noDetailedCollection, "no-details", false, "Disable detailed collection including logs and more", nil) + + // Icinga 2 specific arguments + args.NewPromptStringVar(&icinga2.APICred.Username, "icinga2-api-user", "", "Icinga 2: Username of global API user to collect data about Icinga 2 Infrastructure", false, icinga2Enabled) + args.NewPromptStringVar(&icinga2.APICred.Password, "icinga2-api-pass", "", "Icinga 2: Password for global API user to collect data about Icinga 2 Infrastructure", false, icinga2Enabled) + args.NewPromptStringSliceVar(&icinga2.APIEndpoints, "icinga2-api-endpoints", []string{}, "Icinga 2: Comma separated list of API Endpoints (including port) to collect data from. FQDN or IP address must be reachable. (Example: i2-master01.local:5665)", false, icinga2Enabled) + + flag.CommandLine.SortFlags = false + + // Output a proper help message with details + flag.Usage = func() { + _, _ = fmt.Fprintf(os.Stderr, "%s\n\n%s\n\n", Product, strings.Trim(Readme, "\n")) + + _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + + flag.PrintDefaults() + } + + // Parse flags from command-line + flag.Parse() + + if printVersion { + fmt.Println(Product, "version", getBuildInfo()) //nolint:forbidigo + os.Exit(0) + } + + // Start interactive wizard if interactive is enabled + if !arguments.NonInteractive { + initErrors = args.CollectArgsFromStdin(strings.Join(moduleOrder, ",")) + } + + // Verify enabled modules + for _, name := range enabledModules { + if _, ok := modules[name]; !ok { + logrus.Fatal("Unknown module to enable: ", name) + } + } +} + +// buildFileName returns a filename to store the output of support collector. +func buildFileName() string { + return FilePrefix + "_" + util.GetHostnameWithoutDomain() + "_" + time.Now().Format("20060102-1504") + ".zip" +} + +func icinga2Enabled() bool { + for _, name := range enabledModules { + if name == "icinga2" { + return true + } + } + + return false +} + +func main() { + // Initialize new collection c, closeCollection := NewCollection(outputFile) + // Close collection defer closeCollection() + // Check for errors in init() + if len(initErrors) > 0 { + for _, err := range initErrors { + c.Log.Info(err) + } + } + // Initialize new metrics and defer function to save it to json metric = metrics.New(getVersion()) defer func() { @@ -183,55 +258,6 @@ func main() { c.Log.Infof("Generated ZIP file located at %s", path) } -func handleArguments() { - // arguments for collection handling - flag.StringSliceVar(&enabledModules, "enable", moduleOrder, "List of enabled module") - flag.StringSliceVar(&disabledModules, "disable", []string{}, "List of disabled module") - flag.StringVarP(&outputFile, "output", "o", buildFileName(), "Output file for the ZIP content") - flag.BoolVar(&noDetailedCollection, "nodetails", false, "Disable detailed collection including logs and more") - flag.StringArrayVar(&extraObfuscators, "hide", []string{}, "List of additional strings to obfuscate. Can be used multiple times and supports regex.") //nolint:lll - flag.DurationVar(&commandTimeout, "command-timeout", commandTimeout, "Timeout for command execution in modules") - - // api credentials for icinga 2 modules - flag.StringVar(&icinga2.APICred.Username, "icinga2-api-user", "", "Username of global Icinga 2 API user to collect data about Icinga 2 Infrastructure") //nolint:lll - flag.StringVar(&icinga2.APICred.Password, "icinga2-api-pass", "", "Password for global Icinga 2 API user to collect data about Icinga 2 Infrastructure") //nolint:lll - flag.StringSliceVar(&icinga2.APIEndpoints, "icinga2-api-endpoints", []string{}, "List of Icinga 2 API Endpoints (including port) to collect data from. FQDN or IP address must be reachable. (Example: i2-master01.local:5665)") //nolint:lll - - // basic arguments - flag.BoolVarP(&printVersion, "version", "V", false, "Print version and exit") - flag.BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging") - - flag.CommandLine.SortFlags = false - - // Output a proper help message with details - flag.Usage = func() { - _, _ = fmt.Fprintf(os.Stderr, "%s\n\n%s\n\n", Product, strings.Trim(Readme, "\n")) - - _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - - flag.PrintDefaults() - } - - flag.Parse() - - if printVersion { - fmt.Println(Product, "version", getBuildInfo()) //nolint:forbidigo - os.Exit(0) - } - - // Verify enabled modules - for _, name := range enabledModules { - if _, ok := modules[name]; !ok { - logrus.Fatal("Unknown module to enable: ", name) - } - } -} - -// buildFileName returns a filename to store the output of support collector. -func buildFileName() string { - return util.GetHostnameWithoutDomain() + "-" + FilePrefix + "-" + time.Now().Format("20060102-1504") + ".zip" -} - // NewCollection starts a new collection. outputFile will be created. // // Collection and cleanup function to defer are returned diff --git a/modules/ansible/collector.go b/modules/ansible/collector.go index 19c69d1..c4936b5 100644 --- a/modules/ansible/collector.go +++ b/modules/ansible/collector.go @@ -1,7 +1,7 @@ package ansible import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -22,19 +22,8 @@ var commands = map[string][]string{ "version.txt": {"ansible", "--version"}, } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find ansible") return } diff --git a/modules/ansible/collector_test.go b/modules/ansible/collector_test.go index b534805..f497acf 100644 --- a/modules/ansible/collector_test.go +++ b/modules/ansible/collector_test.go @@ -2,6 +2,7 @@ package ansible import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find ansible in the test environment") + return + } + buf := &bytes.Buffer{} c := collection.New(buf) diff --git a/modules/corosync/collector.go b/modules/corosync/collector.go index 3b7b5a7..1c85e23 100644 --- a/modules/corosync/collector.go +++ b/modules/corosync/collector.go @@ -1,7 +1,7 @@ package corosync import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -38,19 +38,8 @@ var commands = map[string][]string{ "version.txt": {"corosync", "-v"}, } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find corosync") return } diff --git a/modules/corosync/collector_test.go b/modules/corosync/collector_test.go index 222f42c..8fc3596 100644 --- a/modules/corosync/collector_test.go +++ b/modules/corosync/collector_test.go @@ -2,6 +2,7 @@ package corosync import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find corosync in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/elastic/collector.go b/modules/elastic/collector.go index 1560434..884a02a 100644 --- a/modules/elastic/collector.go +++ b/modules/elastic/collector.go @@ -1,7 +1,7 @@ package elastic import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -42,19 +42,8 @@ var obfuscators = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(?:password|keypassphrase).*\s*:\s*(.*)`, `yaml`), } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find Elastic Stack") return } diff --git a/modules/elastic/collector_test.go b/modules/elastic/collector_test.go index 1367b27..7a7ca35 100644 --- a/modules/elastic/collector_test.go +++ b/modules/elastic/collector_test.go @@ -11,6 +11,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find elastic in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/foreman/collector.go b/modules/foreman/collector.go index 13087b2..5ee2719 100644 --- a/modules/foreman/collector.go +++ b/modules/foreman/collector.go @@ -1,7 +1,7 @@ package foreman import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -10,6 +10,10 @@ import ( const ModuleName = "foreman" +var relevantPaths = []string{ + "/etc/foreman", +} + var files = []string{ "/etc/foreman", "/etc/foreman-installer", @@ -22,18 +26,13 @@ var detailedFiles = []string{ "/var/log/foreman-proxy", } -func detect() bool { - _, err := os.Stat("/etc/foreman") - return err == nil -} - var obfuscaters = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(?:password)\s*:\s*(.*)`, "yml"), obfuscate.NewFile(`(?i)(?:ENCRYPTION_KEY)\s*=\s*(.*)`, "rb"), } func Collect(c *collection.Collection) { - if !detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find Foreman") return } diff --git a/modules/foreman/collector_test.go b/modules/foreman/collector_test.go index 9689139..271cf54 100644 --- a/modules/foreman/collector_test.go +++ b/modules/foreman/collector_test.go @@ -11,13 +11,13 @@ import ( ) func TestCollect(t *testing.T) { - c := collection.New(&bytes.Buffer{}) - - if !detect() { + if !util.ModuleExists(relevantPaths) { t.Skip("could not find foreman in the test environment") return } + c := collection.New(&bytes.Buffer{}) + Collect(c) err := c.Close() diff --git a/modules/grafana/collector.go b/modules/grafana/collector.go index 08f30d8..1442b08 100644 --- a/modules/grafana/collector.go +++ b/modules/grafana/collector.go @@ -1,7 +1,7 @@ package grafana import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -35,19 +35,8 @@ var obfuscators = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(?:password|token|key|secret).*\s*=\s*(.*)`, `toml`), } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find grafana") return } diff --git a/modules/grafana/collector_test.go b/modules/grafana/collector_test.go index b7e7722..91c354f 100644 --- a/modules/grafana/collector_test.go +++ b/modules/grafana/collector_test.go @@ -2,6 +2,7 @@ package grafana import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find grafana in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/graphite/collector.go b/modules/graphite/collector.go index dfe106e..fc787aa 100644 --- a/modules/graphite/collector.go +++ b/modules/graphite/collector.go @@ -1,7 +1,6 @@ package graphite import ( - "os" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -47,19 +46,8 @@ var processFilter = []string{ "carbon", } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find graphite") return } diff --git a/modules/graphite/collector_test.go b/modules/graphite/collector_test.go index 64d6be5..053b7c4 100644 --- a/modules/graphite/collector_test.go +++ b/modules/graphite/collector_test.go @@ -2,6 +2,7 @@ package graphite import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find graphite in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/graylog/collector.go b/modules/graylog/collector.go index ba5596d..e46f1e4 100644 --- a/modules/graylog/collector.go +++ b/modules/graylog/collector.go @@ -1,7 +1,7 @@ package graylog import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -26,19 +26,8 @@ var obfuscators = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(?:password_secret|root_password_sha2|elasticsearch_hosts|mongodb_uri).*\s*=\s*(.*)`, `conf`), } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find Graylog") return } diff --git a/modules/graylog/collector_test.go b/modules/graylog/collector_test.go index b657aee..a2fdbde 100644 --- a/modules/graylog/collector_test.go +++ b/modules/graylog/collector_test.go @@ -11,6 +11,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find graylog in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/icinga2/collector.go b/modules/icinga2/collector.go index 304f132..dc87034 100644 --- a/modules/icinga2/collector.go +++ b/modules/icinga2/collector.go @@ -1,8 +1,8 @@ package icinga2 import ( + "github.com/NETWAYS/support-collector/internal/util" "os" - "os/exec" "path/filepath" "regexp" @@ -12,6 +12,11 @@ import ( const ModuleName = "icinga2" +var relevantPaths = []string{ + "/etc/icinga2", + "/var/lib/icinga2", +} + var files = []string{ "/etc/icinga2", "/var/lib/icinga2/api/packages/_api/active-stage", @@ -64,11 +69,6 @@ var obfuscators = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(?:password|community)(.*)`, `log`), } -func detectIcinga() bool { - _, err := exec.LookPath("icinga2") - return err == nil -} - func detectIcingaVersion(version string) string { result := regexp.MustCompile(`\(version:\s+r(\d+.\d+.\d+)`).FindStringSubmatch(version) @@ -78,7 +78,7 @@ func detectIcingaVersion(version string) string { func Collect(c *collection.Collection) { var icinga2version string - if !detectIcinga() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find icinga2") return } diff --git a/modules/icinga2/collector_test.go b/modules/icinga2/collector_test.go index 8176d50..b2a5fbb 100644 --- a/modules/icinga2/collector_test.go +++ b/modules/icinga2/collector_test.go @@ -12,6 +12,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find icinga2 in the test environment") + return + } + file, err := os.ReadFile("testdata/icinga-version.txt") if err != nil { t.Skip("cant read version file") @@ -22,7 +27,7 @@ func TestCollect(t *testing.T) { t.Skip("cant detect icinga2 version") } - if !detectIcinga() { + if !util.ModuleExists(relevantPaths) { t.Skip("could not find icinga2 in the test environment") return } diff --git a/modules/icingadb/collector.go b/modules/icingadb/collector.go index f4a4a69..00489a1 100644 --- a/modules/icingadb/collector.go +++ b/modules/icingadb/collector.go @@ -1,6 +1,7 @@ package icingadb import ( + "github.com/NETWAYS/support-collector/internal/util" "os" "path/filepath" @@ -48,19 +49,8 @@ var obfuscators = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(?:password)\s*=\s*(.*)`, `yml`), } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find IcingaDB") return } diff --git a/modules/icingadb/collector_test.go b/modules/icingadb/collector_test.go index e0a7d56..f6afe41 100644 --- a/modules/icingadb/collector_test.go +++ b/modules/icingadb/collector_test.go @@ -2,6 +2,7 @@ package icingadb import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find icingadb in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/icingadirector/collector.go b/modules/icingadirector/collector.go index dbb3f4c..a8aba24 100644 --- a/modules/icingadirector/collector.go +++ b/modules/icingadirector/collector.go @@ -1,7 +1,7 @@ package icingadirector import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -31,15 +31,9 @@ var journalctlLogs = map[string]collection.JournalElement{ "journalctl-director.txt": {Service: "icinga-director.service"}, } -// Detect if Icinga Director is installed on the system. -func Detect() bool { - _, err := os.Stat(InstallationPath) - return err == nil -} - // Collect data for Icinga Director. func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists([]string{InstallationPath}) { c.Log.Info("Could not find Icinga Director") return } diff --git a/modules/icingadirector/collector_test.go b/modules/icingadirector/collector_test.go index 6db9789..d911a7c 100644 --- a/modules/icingadirector/collector_test.go +++ b/modules/icingadirector/collector_test.go @@ -2,6 +2,7 @@ package icingadirector import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists([]string{InstallationPath}) { + t.Skip("could not find icingadirector in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/icingaweb2/collector.go b/modules/icingaweb2/collector.go index 961ba9a..3e85231 100644 --- a/modules/icingaweb2/collector.go +++ b/modules/icingaweb2/collector.go @@ -1,6 +1,7 @@ package icingaweb2 import ( + "github.com/NETWAYS/support-collector/internal/util" "os" "path/filepath" @@ -61,21 +62,9 @@ var obfuscators = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(?:bind_pw|password|token)\s*=\s*(.*)`, `ini`), } -// Detect if icingaweb2 is installed on the system. -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - // Collect data for icingaweb2. func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find icingaweb2") return } diff --git a/modules/icingaweb2/collector_test.go b/modules/icingaweb2/collector_test.go index 9f80a90..fa528be 100644 --- a/modules/icingaweb2/collector_test.go +++ b/modules/icingaweb2/collector_test.go @@ -11,7 +11,7 @@ import ( ) func TestCollect(t *testing.T) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { t.Skip("could not find icingaweb2 in the test environment") return } diff --git a/modules/influxdb/collector.go b/modules/influxdb/collector.go index 42371a1..2840ccd 100644 --- a/modules/influxdb/collector.go +++ b/modules/influxdb/collector.go @@ -1,7 +1,7 @@ package influxdb import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -22,19 +22,8 @@ var detailedFiles = []string{ "/var/log/influxdb", } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find InfluxDB") return } diff --git a/modules/influxdb/collector_test.go b/modules/influxdb/collector_test.go index e7173fb..2961bc1 100644 --- a/modules/influxdb/collector_test.go +++ b/modules/influxdb/collector_test.go @@ -2,6 +2,7 @@ package influxdb import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find influxdb in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/keepalived/collector.go b/modules/keepalived/collector.go index 640bdce..32cf50b 100644 --- a/modules/keepalived/collector.go +++ b/modules/keepalived/collector.go @@ -1,7 +1,7 @@ package keepalived import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -35,19 +35,8 @@ var obfuscators = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(auth_pass) (.*)`, `conf`), } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find keepalived") return } diff --git a/modules/keepalived/collector_test.go b/modules/keepalived/collector_test.go index 934eac1..0bb9b24 100644 --- a/modules/keepalived/collector_test.go +++ b/modules/keepalived/collector_test.go @@ -11,6 +11,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find keepalived in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/mongodb/collector.go b/modules/mongodb/collector.go index 88dab8b..a04c269 100644 --- a/modules/mongodb/collector.go +++ b/modules/mongodb/collector.go @@ -1,7 +1,7 @@ package mongodb import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -40,19 +40,8 @@ var obfuscators = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(?:password).*\s*:\s*(.*)`, `conf`), } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find mongodb") return } diff --git a/modules/mongodb/collector_test.go b/modules/mongodb/collector_test.go index e205f81..379c640 100644 --- a/modules/mongodb/collector_test.go +++ b/modules/mongodb/collector_test.go @@ -11,6 +11,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find mongodb in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/mysql/collector.go b/modules/mysql/collector.go index eb48913..77f075b 100644 --- a/modules/mysql/collector.go +++ b/modules/mysql/collector.go @@ -44,7 +44,7 @@ var obfuscators = []*obfuscate.Obfuscator{ } // Detect if a MySQL or MariaDB daemon appears to be running. -func Detect() string { +func getService() string { for _, name := range possibleServices { _, err := collection.GetServiceStatusRaw(name) if err == nil { @@ -57,7 +57,7 @@ func Detect() string { // Collect data for MySQL or MariaDB. func Collect(c *collection.Collection) { - service := Detect() + service := getService() if service == "" { c.Log.Info("Could not a running MySQL or MariaDB service") return diff --git a/modules/mysql/collector_test.go b/modules/mysql/collector_test.go index fe30a90..ce41f4d 100644 --- a/modules/mysql/collector_test.go +++ b/modules/mysql/collector_test.go @@ -9,6 +9,7 @@ import ( ) func TestCollect(t *testing.T) { + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/postgresql/collector.go b/modules/postgresql/collector.go index 35228bb..d267bd0 100644 --- a/modules/postgresql/collector.go +++ b/modules/postgresql/collector.go @@ -1,7 +1,7 @@ package postgresql import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -32,19 +32,8 @@ var possibleServices = []string{ "postgresql", } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find PostgreSQL") return } diff --git a/modules/postgresql/collector_test.go b/modules/postgresql/collector_test.go index 3b4440b..a274bc7 100644 --- a/modules/postgresql/collector_test.go +++ b/modules/postgresql/collector_test.go @@ -2,6 +2,7 @@ package postgresql import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find postgresql in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/prometheus/collector.go b/modules/prometheus/collector.go index e9d39aa..dd0e6e6 100644 --- a/modules/prometheus/collector.go +++ b/modules/prometheus/collector.go @@ -1,7 +1,7 @@ package prometheus import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -44,19 +44,8 @@ var obfuscators = []*obfuscate.Obfuscator{ obfuscate.NewFile(`(?i)(?:password|secret).*\s*:\s*(.*)`, `yaml`), } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find Prometheus") return } diff --git a/modules/prometheus/collector_test.go b/modules/prometheus/collector_test.go index 4236bef..8e70b96 100644 --- a/modules/prometheus/collector_test.go +++ b/modules/prometheus/collector_test.go @@ -11,6 +11,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find prometheus in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/puppet/collector.go b/modules/puppet/collector.go index fee3836..be15830 100644 --- a/modules/puppet/collector.go +++ b/modules/puppet/collector.go @@ -1,7 +1,7 @@ package puppet import ( - "os" + "github.com/NETWAYS/support-collector/internal/util" "path/filepath" "github.com/NETWAYS/support-collector/internal/collection" @@ -33,19 +33,8 @@ var commands = map[string][]string{ "puppet-module-list.txt": {"puppet", "module", "list"}, } -func Detect() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !Detect() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find puppet") return } diff --git a/modules/puppet/collector_test.go b/modules/puppet/collector_test.go index 791ba31..f0765fa 100644 --- a/modules/puppet/collector_test.go +++ b/modules/puppet/collector_test.go @@ -2,6 +2,7 @@ package puppet import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find puppet in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c) diff --git a/modules/webservers/collector.go b/modules/webservers/collector.go index 197de6f..1c6cc3f 100644 --- a/modules/webservers/collector.go +++ b/modules/webservers/collector.go @@ -1,6 +1,7 @@ package webservers import ( + "github.com/NETWAYS/support-collector/internal/util" "os" "path/filepath" @@ -43,19 +44,8 @@ var possibleDaemons = []string{ "/usr/lib/systemd/system/httpd.service", } -func DetectWebservers() bool { - for _, path := range relevantPaths { - _, err := os.Stat(path) - if err == nil { - return true - } - } - - return false -} - func Collect(c *collection.Collection) { - if !DetectWebservers() { + if !util.ModuleExists(relevantPaths) { c.Log.Info("Could not find webservers") return } diff --git a/modules/webservers/collector_test.go b/modules/webservers/collector_test.go index 849c182..4edfcbe 100644 --- a/modules/webservers/collector_test.go +++ b/modules/webservers/collector_test.go @@ -2,6 +2,7 @@ package webservers import ( "bytes" + "github.com/NETWAYS/support-collector/internal/util" "testing" "github.com/NETWAYS/support-collector/internal/collection" @@ -9,6 +10,11 @@ import ( ) func TestCollect(t *testing.T) { + if !util.ModuleExists(relevantPaths) { + t.Skip("could not find webservers in the test environment") + return + } + c := collection.New(&bytes.Buffer{}) Collect(c)