From a85426ac5864ccf4b78da044452c139ebe3b9113 Mon Sep 17 00:00:00 2001 From: Muhammad Talal Anwar Date: Sat, 11 Jan 2020 19:43:46 +0100 Subject: [PATCH 1/9] Bump kingpin to master --- go.mod | 2 +- go.sum | 4 +- .../github.com/alecthomas/kingpin/.travis.yml | 12 +++- .../github.com/alecthomas/kingpin/README.md | 71 +++++++++++++------ vendor/github.com/alecthomas/kingpin/app.go | 13 ++-- vendor/github.com/alecthomas/kingpin/args.go | 21 ++++++ vendor/github.com/alecthomas/kingpin/cmd.go | 47 +++++++++++- vendor/github.com/alecthomas/kingpin/envar.go | 9 +-- vendor/github.com/alecthomas/kingpin/flags.go | 24 +++++++ .../github.com/alecthomas/kingpin/global.go | 2 + vendor/github.com/alecthomas/kingpin/go.sum | 10 +++ vendor/github.com/alecthomas/kingpin/model.go | 60 ++++++++++++---- .../github.com/alecthomas/kingpin/parser.go | 10 ++- .../alecthomas/kingpin/templates.go | 38 +++++----- vendor/github.com/alecthomas/kingpin/usage.go | 27 ++++--- vendor/modules.txt | 2 +- 16 files changed, 265 insertions(+), 87 deletions(-) create mode 100644 vendor/github.com/alecthomas/kingpin/go.sum diff --git a/go.mod b/go.mod index a66821a..1052be4 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/sapcc/swift-health-exporter go 1.13 require ( - github.com/alecthomas/kingpin v2.2.6+incompatible + github.com/alecthomas/kingpin v1.3.8-0.20191202215629-0ce3bba646ba github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect github.com/prometheus/client_golang v1.3.0 github.com/sapcc/go-bits v0.0.0-20191125140305-f4a863758644 diff --git a/go.sum b/go.sum index 1548d74..bfc4713 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/alecthomas/kingpin v2.2.6+incompatible h1:5svnBTFgJjZvGKyYBtMB0+m5wvrbUHiqye8wRJMlnYI= -github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= +github.com/alecthomas/kingpin v1.3.8-0.20191202215629-0ce3bba646ba h1:RgaHYGQoSwiMAgn0tXwcaieX1kaOQrPC0NX1OD8rZgk= +github.com/alecthomas/kingpin v1.3.8-0.20191202215629-0ce3bba646ba/go.mod h1:b6br6/pDFSfMkBgC96TbpOji05q5pa+v5rIlS0Y6XtI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= diff --git a/vendor/github.com/alecthomas/kingpin/.travis.yml b/vendor/github.com/alecthomas/kingpin/.travis.yml index e564b74..9c45bac 100644 --- a/vendor/github.com/alecthomas/kingpin/.travis.yml +++ b/vendor/github.com/alecthomas/kingpin/.travis.yml @@ -1,4 +1,14 @@ sudo: false language: go install: go get -t -v ./... -go: 1.2 +go: + - 1.2.x + - 1.3.x + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x diff --git a/vendor/github.com/alecthomas/kingpin/README.md b/vendor/github.com/alecthomas/kingpin/README.md index 498704c..19d142d 100644 --- a/vendor/github.com/alecthomas/kingpin/README.md +++ b/vendor/github.com/alecthomas/kingpin/README.md @@ -250,6 +250,17 @@ func main() { } ``` +#### Reading arguments from a file +Kingpin supports reading arguments from a file. +Creat a file with the corresponding arguments: +``` +echo -t=5\n > args +``` +And now supply it: +``` +$ ping @args +``` + ### Complex Example Kingpin can also produce complex command-line applications with global flags, @@ -377,8 +388,8 @@ var ( func main() { switch kingpin.Parse() { - case "delete user": - case "delete post": + case deleteUserCommand.FullCommand(): + case deletePostCommand.FullCommand(): } } ``` @@ -514,35 +525,35 @@ ips := IPList(kingpin.Arg("ips", "IP addresses to ping.")) ### Bash/ZSH Shell Completion -By default, all flags and commands/subcommands generate completions +By default, all flags and commands/subcommands generate completions internally. -Out of the box, CLI tools using kingpin should be able to take advantage -of completion hinting for flags and commands. By specifying -`--completion-bash` as the first argument, your CLI tool will show -possible subcommands. By ending your argv with `--`, hints for flags +Out of the box, CLI tools using kingpin should be able to take advantage +of completion hinting for flags and commands. By specifying +`--completion-bash` as the first argument, your CLI tool will show +possible subcommands. By ending your argv with `--`, hints for flags will be shown. -To allow your end users to take advantage you must package a -`/etc/bash_completion.d` script with your distribution (or the equivalent -for your target platform/shell). An alternative is to instruct your end +To allow your end users to take advantage you must package a +`/etc/bash_completion.d` script with your distribution (or the equivalent +for your target platform/shell). An alternative is to instruct your end user to source a script from their `bash_profile` (or equivalent). Fortunately Kingpin makes it easy to generate or source a script for use -with end users shells. `./yourtool --completion-script-bash` and +with end users shells. `./yourtool --completion-script-bash` and `./yourtool --completion-script-zsh` will generate these scripts for you. **Installation by Package** -For the best user experience, you should bundle your pre-created -completion script with your CLI tool and install it inside -`/etc/bash_completion.d` (or equivalent). A good suggestion is to add -this as an automated step to your build pipeline, in the implementation +For the best user experience, you should bundle your pre-created +completion script with your CLI tool and install it inside +`/etc/bash_completion.d` (or equivalent). A good suggestion is to add +this as an automated step to your build pipeline, in the implementation is improved for bug fixed. **Installation by `bash_profile`** -Alternatively, instruct your users to add an additional statement to +Alternatively, instruct your users to add an additional statement to their `bash_profile` (or equivalent): ``` @@ -558,13 +569,13 @@ eval "$(your-cli-tool --completion-script-zsh)" #### Additional API To provide more flexibility, a completion option API has been exposed for flags to allow user defined completion options, to extend -completions further than just EnumVar/Enum. +completions further than just EnumVar/Enum. **Provide Static Options** -When using an `Enum` or `EnumVar`, users are limited to only the options -given. Maybe we wish to hint possible options to the user, but also +When using an `Enum` or `EnumVar`, users are limited to only the options +given. Maybe we wish to hint possible options to the user, but also allow them to provide their own custom option. `HintOptions` gives this functionality to flags. @@ -577,7 +588,7 @@ app.Flag("port", "Provide a port to connect to"). ``` **Provide Dynamic Options** -Consider the case that you needed to read a local database or a file to +Consider the case that you needed to read a local database or a file to provide suggestions. You can dynamically generate the options ``` @@ -596,13 +607,13 @@ app.Flag("flag-1", "").HintAction(listHosts).String() **EnumVar/Enum** When using `Enum` or `EnumVar`, any provided options will be automatically -used for bash autocompletion. However, if you wish to provide a subset or +used for bash autocompletion. However, if you wish to provide a subset or different options, you can use `HintOptions` or `HintAction` which will override the default completion options for `Enum`/`EnumVar`. **Examples** -You can see an in depth example of the completion API within +You can see an in depth example of the completion API within `examples/completion/main.go` @@ -610,6 +621,22 @@ You can see an in depth example of the completion API within `kingpin.CommandLine.HelpFlag.Short('h')` +Short help is also available when creating a more complicated app: + +```go +var ( + app = kingpin.New("chat", "A command-line chat application.") + // ... +) + +func main() { + app.HelpFlag.Short('h') + switch kingpin.MustParse(app.Parse(os.Args[1:])) { + // ... + } +} +``` + ### Custom help Kingpin v2 supports templatised help using the text/template library (actually, [a fork](https://github.com/alecthomas/template)). diff --git a/vendor/github.com/alecthomas/kingpin/app.go b/vendor/github.com/alecthomas/kingpin/app.go index 1a1a5ef..c3a2ac4 100644 --- a/vendor/github.com/alecthomas/kingpin/app.go +++ b/vendor/github.com/alecthomas/kingpin/app.go @@ -6,6 +6,7 @@ import ( "os" "regexp" "strings" + "text/template" ) var ( @@ -32,6 +33,7 @@ type Application struct { errorWriter io.Writer // Destination for errors. usageWriter io.Writer // Destination for usage usageTemplate string + usageFuncs template.FuncMap validator ApplicationValidator terminate func(status int) // See Terminate() noInterspersed bool // can flags be interspersed with args (or must they come first) @@ -153,6 +155,12 @@ func (a *Application) UsageTemplate(template string) *Application { return a } +// UsageFuncs adds extra functions that can be used in the usage template. +func (a *Application) UsageFuncs(funcs template.FuncMap) *Application { + a.usageFuncs = funcs + return a +} + // Validate sets a validation function to run when parsing. func (a *Application) Validate(validator ApplicationValidator) *Application { a.validator = validator @@ -496,11 +504,6 @@ func (a *Application) setValues(context *ParseContext) (selected []string, err e } case *CmdClause: - if clause.validator != nil { - if err = clause.validator(clause); err != nil { - return - } - } selected = append(selected, clause.name) lastCmd = clause } diff --git a/vendor/github.com/alecthomas/kingpin/args.go b/vendor/github.com/alecthomas/kingpin/args.go index 3400694..54e4107 100644 --- a/vendor/github.com/alecthomas/kingpin/args.go +++ b/vendor/github.com/alecthomas/kingpin/args.go @@ -71,6 +71,8 @@ type ArgClause struct { name string help string defaultValues []string + placeholder string + hidden bool required bool } @@ -120,6 +122,19 @@ func (a *ArgClause) consumesRemainder() bool { return false } +// Hidden hides the argument from usage but still allows it to be used. +func (a *ArgClause) Hidden() *ArgClause { + a.hidden = true + return a +} + +// PlaceHolder sets the place-holder string used for arg values in the help. The +// default behaviour is to use the arg name between < > brackets. +func (a *ArgClause) PlaceHolder(value string) *ArgClause { + a.placeholder = value + return a +} + // Required arguments must be input by the user. They can not have a Default() value provided. func (a *ArgClause) Required() *ArgClause { a.required = true @@ -173,6 +188,12 @@ func (a *ArgClause) HintOptions(options ...string) *ArgClause { return a } +// Help sets the help message. +func (a *ArgClause) Help(help string) *ArgClause { + a.help = help + return a +} + func (a *ArgClause) init() error { if a.required && len(a.defaultValues) > 0 { return fmt.Errorf("required argument '%s' with unusable default value", a.name) diff --git a/vendor/github.com/alecthomas/kingpin/cmd.go b/vendor/github.com/alecthomas/kingpin/cmd.go index 0473b87..5703b95 100644 --- a/vendor/github.com/alecthomas/kingpin/cmd.go +++ b/vendor/github.com/alecthomas/kingpin/cmd.go @@ -21,11 +21,41 @@ func (c *cmdMixin) CmdCompletion(context *ParseContext) []string { // default commands' alternatives, since they weren't listed explicitly // and the user may want to explicitly list something else. argsSatisfied := 0 + allSatisfied := false +ElementLoop: for _, el := range context.Elements { switch clause := el.Clause.(type) { case *ArgClause: + // Each new element should reset the previous state + allSatisfied = false + options = nil + if el.Value != nil && *el.Value != "" { + // Get the list of valid options for the last argument + validOptions := c.argGroup.args[argsSatisfied].resolveCompletions() + if len(validOptions) == 0 { + // If there are no options for this argument, + // mark is as allSatisfied as we can't suggest anything + argsSatisfied++ + allSatisfied = true + continue ElementLoop + } + + for _, opt := range validOptions { + if opt == *el.Value { + // We have an exact match + // We don't need to suggest any option + argsSatisfied++ + continue ElementLoop + } + if strings.HasPrefix(opt, *el.Value) { + // If the option match the partially entered argument, add it to the list + options = append(options, opt) + } + } + // Avoid further completion as we have done everything we could argsSatisfied++ + allSatisfied = true } case *CmdClause: options = append(options, clause.completionAlts...) @@ -33,7 +63,7 @@ func (c *cmdMixin) CmdCompletion(context *ParseContext) []string { } } - if argsSatisfied < len(c.argGroup.args) { + if argsSatisfied < len(c.argGroup.args) && !allSatisfied { // Since not all args have been satisfied, show options for the current one options = append(options, c.argGroup.args[argsSatisfied].resolveCompletions()...) } else { @@ -191,6 +221,7 @@ type CmdClause struct { name string aliases []string help string + helpLong string isDefault bool validator CmdClauseValidator hidden bool @@ -252,6 +283,12 @@ func (c *CmdClause) PreAction(action Action) *CmdClause { return c } +// Help sets the help message. +func (c *CmdClause) Help(help string) *CmdClause { + c.help = help + return c +} + func (c *CmdClause) init() error { if err := c.flagGroup.init(c.app.defaultEnvarPrefix()); err != nil { return err @@ -272,3 +309,11 @@ func (c *CmdClause) Hidden() *CmdClause { c.hidden = true return c } + +// HelpLong adds a long help text, which can be used in usage templates. +// For example, to use a longer help text in the command-specific help +// than in the apps root help. +func (c *CmdClause) HelpLong(help string) *CmdClause { + c.helpLong = help + return c +} diff --git a/vendor/github.com/alecthomas/kingpin/envar.go b/vendor/github.com/alecthomas/kingpin/envar.go index c01a27d..44e16de 100644 --- a/vendor/github.com/alecthomas/kingpin/envar.go +++ b/vendor/github.com/alecthomas/kingpin/envar.go @@ -28,18 +28,13 @@ func (e *envarMixin) GetEnvarValue() string { } func (e *envarMixin) GetSplitEnvarValue() []string { - values := make([]string, 0) - envarValue := e.GetEnvarValue() if envarValue == "" { - return values + return []string{} } // Split by new line to extract multiple values, if any. trimmed := envVarValuesTrimmer.ReplaceAllString(envarValue, "") - for _, value := range envVarValuesSplitter.Split(trimmed, -1) { - values = append(values, value) - } - return values + return envVarValuesSplitter.Split(trimmed, -1) } diff --git a/vendor/github.com/alecthomas/kingpin/flags.go b/vendor/github.com/alecthomas/kingpin/flags.go index 8f33721..2b2938b 100644 --- a/vendor/github.com/alecthomas/kingpin/flags.go +++ b/vendor/github.com/alecthomas/kingpin/flags.go @@ -109,6 +109,8 @@ loop: context.Next() + flag.isSetByUser() + fb, ok := flag.value.(boolFlag) if ok && fb.IsBoolFlag() { if invert { @@ -152,6 +154,7 @@ type FlagClause struct { defaultValues []string placeholder string hidden bool + setByUser *bool } func newFlag(name, help string) *FlagClause { @@ -189,6 +192,12 @@ func (f *FlagClause) setDefault() error { return nil } +func (f *FlagClause) isSetByUser() { + if f.setByUser != nil { + *f.setByUser = true + } +} + func (f *FlagClause) needsValue() bool { haveDefault := len(f.defaultValues) > 0 return f.required && !(haveDefault || f.HasEnvarValue()) @@ -246,6 +255,15 @@ func (a *FlagClause) Enum(options ...string) (target *string) { return a.parserMixin.Enum(options...) } +// IsSetByUser let to know if the flag was set by the user +func (f *FlagClause) IsSetByUser(setByUser *bool) *FlagClause { + if setByUser != nil { + *setByUser = false + } + f.setByUser = setByUser + return f +} + // Default values for this flag. They *must* be parseable by the value of the flag. func (f *FlagClause) Default(values ...string) *FlagClause { f.defaultValues = values @@ -300,6 +318,12 @@ func (f *FlagClause) Short(name rune) *FlagClause { return f } +// Help sets the help message. +func (f *FlagClause) Help(help string) *FlagClause { + f.help = help + return f +} + // Bool makes this flag a boolean flag. func (f *FlagClause) Bool() (target *bool) { target = new(bool) diff --git a/vendor/github.com/alecthomas/kingpin/global.go b/vendor/github.com/alecthomas/kingpin/global.go index 10a2913..4d073ea 100644 --- a/vendor/github.com/alecthomas/kingpin/global.go +++ b/vendor/github.com/alecthomas/kingpin/global.go @@ -14,6 +14,8 @@ var ( HelpCommand = CommandLine.HelpCommand // Global version flag. Exposed for user customisation. May be nil. VersionFlag = CommandLine.VersionFlag + // Whether to file expansion with '@' is enabled. + EnableFileExpansion = true ) // Command adds a new command to the default parser. diff --git a/vendor/github.com/alecthomas/kingpin/go.sum b/vendor/github.com/alecthomas/kingpin/go.sum new file mode 100644 index 0000000..5107484 --- /dev/null +++ b/vendor/github.com/alecthomas/kingpin/go.sum @@ -0,0 +1,10 @@ +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +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/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/vendor/github.com/alecthomas/kingpin/model.go b/vendor/github.com/alecthomas/kingpin/model.go index a4ee83b..035d536 100644 --- a/vendor/github.com/alecthomas/kingpin/model.go +++ b/vendor/github.com/alecthomas/kingpin/model.go @@ -8,6 +8,17 @@ import ( // Data model for Kingpin command-line structure. +var ( + ignoreInCount = map[string]bool{ + "help": true, + "help-long": true, + "help-man": true, + "completion-bash": true, + "completion-script-bash": true, + "completion-script-zsh": true, + } +) + type FlagGroupModel struct { Flags []*FlagModel } @@ -15,10 +26,13 @@ type FlagGroupModel struct { func (f *FlagGroupModel) FlagSummary() string { out := []string{} count := 0 + for _, flag := range f.Flags { - if flag.Name != "help" { + + if !ignoreInCount[flag.Name] { count++ } + if flag.Required { if flag.IsBoolFlag() { out = append(out, fmt.Sprintf("--[no-]%s", flag.Name)) @@ -46,6 +60,9 @@ type FlagModel struct { } func (f *FlagModel) String() string { + if f.Value == nil { + return "" + } return f.Value.String() } @@ -81,7 +98,12 @@ func (a *ArgGroupModel) ArgSummary() string { depth := 0 out := []string{} for _, arg := range a.Args { - h := "<" + arg.Name + ">" + var h string + if arg.PlaceHolder != "" { + h = arg.PlaceHolder + } else { + h = "<" + arg.Name + ">" + } if !arg.Required { h = "[" + h depth++ @@ -93,15 +115,21 @@ func (a *ArgGroupModel) ArgSummary() string { } type ArgModel struct { - Name string - Help string - Default []string - Envar string - Required bool - Value Value + Name string + Help string + Default []string + Envar string + PlaceHolder string + Required bool + Hidden bool + Value Value } func (a *ArgModel) String() string { + if a.Value == nil { + return "" + } + return a.Value.String() } @@ -123,6 +151,7 @@ type CmdModel struct { Name string Aliases []string Help string + HelpLong string FullCommand string Depth int Hidden bool @@ -168,12 +197,14 @@ func (a *argGroup) Model() *ArgGroupModel { func (a *ArgClause) Model() *ArgModel { return &ArgModel{ - Name: a.name, - Help: a.help, - Default: a.defaultValues, - Envar: a.envar, - Required: a.required, - Value: a.value, + Name: a.name, + Help: a.help, + Default: a.defaultValues, + Envar: a.envar, + PlaceHolder: a.placeholder, + Required: a.required, + Hidden: a.hidden, + Value: a.value, } } @@ -216,6 +247,7 @@ func (c *CmdClause) Model() *CmdModel { Name: c.name, Aliases: c.aliases, Help: c.help, + HelpLong: c.helpLong, Depth: depth, Hidden: c.hidden, Default: c.isDefault, diff --git a/vendor/github.com/alecthomas/kingpin/parser.go b/vendor/github.com/alecthomas/kingpin/parser.go index 2a18351..9b42c47 100644 --- a/vendor/github.com/alecthomas/kingpin/parser.go +++ b/vendor/github.com/alecthomas/kingpin/parser.go @@ -144,9 +144,7 @@ func (p *ParseContext) mergeFlags(flags *flagGroup) { } func (p *ParseContext) mergeArgs(args *argGroup) { - for _, arg := range args.args { - p.arguments.args = append(p.arguments.args, arg) - } + p.arguments.args = append(p.arguments.args, args.args...) } func (p *ParseContext) EOL() bool { @@ -192,7 +190,7 @@ func (p *ParseContext) Next() *Token { if strings.HasPrefix(arg, "-") { if len(arg) == 1 { - return &Token{Index: p.argi, Type: TokenShort} + return &Token{Index: p.argi, Type: TokenArg} } shortRune, size := utf8.DecodeRuneInString(arg[1:]) short := string(shortRune) @@ -214,7 +212,7 @@ func (p *ParseContext) Next() *Token { p.args = append([]string{"-" + arg[size+1:]}, p.args...) } return &Token{p.argi, TokenShort, short} - } else if strings.HasPrefix(arg, "@") { + } else if EnableFileExpansion && strings.HasPrefix(arg, "@") { expanded, err := ExpandArgsFromFile(arg[1:]) if err != nil { return &Token{p.argi, TokenError, err.Error()} @@ -281,7 +279,7 @@ func ExpandArgsFromFile(filename string) (out []string, err error) { scanner := bufio.NewScanner(r) for scanner.Scan() { line := scanner.Text() - if strings.HasPrefix(line, "#") { + if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" { continue } out = append(out, line) diff --git a/vendor/github.com/alecthomas/kingpin/templates.go b/vendor/github.com/alecthomas/kingpin/templates.go index 97b5c9f..98f9a5e 100644 --- a/vendor/github.com/alecthomas/kingpin/templates.go +++ b/vendor/github.com/alecthomas/kingpin/templates.go @@ -3,7 +3,7 @@ package kingpin // Default usage template. var DefaultUsageTemplate = `{{define "FormatCommand"}}\ {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ -{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ +{{range .Args}}{{if not .Hidden}} {{if not .Required}}[{{end}}{{if .PlaceHolder}}{{.PlaceHolder}}{{else}}<{{.Name}}>{{end}}{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}{{end}}\ {{end}}\ {{define "FormatCommands"}}\ @@ -50,7 +50,7 @@ Commands: // Usage template where command's optional flags are listed separately var SeparateOptionalFlagsUsageTemplate = `{{define "FormatCommand"}}\ {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ -{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ +{{range .Args}}{{if not .Hidden}} {{if not .Required}}[{{end}}{{if .PlaceHolder}}{{.PlaceHolder}}{{else}}<{{.Name}}>{{end}}{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}{{end}}\ {{end}}\ {{define "FormatCommands"}}\ @@ -101,7 +101,7 @@ Commands: // Usage template with compactly formatted commands. var CompactUsageTemplate = `{{define "FormatCommand"}}\ {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ -{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ +{{range .Args}}{{if not .Hidden}} {{if not .Required}}[{{end}}{{if .PlaceHolder}}{{.PlaceHolder}}{{else}}<{{.Name}}>{{end}}{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}{{end}}\ {{end}}\ {{define "FormatCommandList"}}\ @@ -158,7 +158,7 @@ var ManPageTemplate = `{{define "FormatFlags"}}\ {{define "FormatCommand"}}\ {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ -{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}{{if .Default}}*{{end}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ +{{range .Args}}{{if not .Hidden}} {{if not .Required}}[{{end}}{{if .PlaceHolder}}{{.PlaceHolder}}{{else}}<{{.Name}}>{{end}}{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}{{end}}\ {{end}}\ {{define "FormatCommands"}}\ @@ -196,7 +196,7 @@ var ManPageTemplate = `{{define "FormatFlags"}}\ // Default usage template. var LongHelpTemplate = `{{define "FormatCommand"}}\ {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ -{{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ +{{range .Args}}{{if not .Hidden}} {{if not .Required}}[{{end}}{{if .PlaceHolder}}{{.PlaceHolder}}{{else}}<{{.Name}}>{{end}}{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}{{end}}\ {{end}}\ {{define "FormatCommands"}}\ @@ -237,26 +237,26 @@ _{{.App.Name}}_bash_autocomplete() { local cur prev opts base COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" - opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} ) + opts=$( ${COMP_WORDS[0]} --completion-bash "${COMP_WORDS[@]:1:$COMP_CWORD}" ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 } -complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}} +complete -F _{{.App.Name}}_bash_autocomplete -o default {{.App.Name}} ` -var ZshCompletionTemplate = ` -#compdef {{.App.Name}} -autoload -U compinit && compinit -autoload -U bashcompinit && bashcompinit +var ZshCompletionTemplate = `#compdef {{.App.Name}} -_{{.App.Name}}_bash_autocomplete() { - local cur prev opts base - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} ) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 +_{{.App.Name}}() { + local matches=($(${words[1]} --completion-bash "${(@)words[1,$CURRENT]}")) + compadd -a matches + + if [[ $compstate[nmatches] -eq 0 && $words[$CURRENT] != -* ]]; then + _files + fi } -complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}} + +if [[ "$(basename -- ${(%):-%x})" != "_{{.App.Name}}" ]]; then + compdef _{{.App.Name}} {{.App.Name}} +fi ` diff --git a/vendor/github.com/alecthomas/kingpin/usage.go b/vendor/github.com/alecthomas/kingpin/usage.go index 44af6f6..59adc30 100644 --- a/vendor/github.com/alecthomas/kingpin/usage.go +++ b/vendor/github.com/alecthomas/kingpin/usage.go @@ -64,11 +64,11 @@ func formatAppUsage(app *ApplicationModel) string { func formatCmdUsage(app *ApplicationModel, cmd *CmdModel) string { s := []string{app.Name, cmd.String()} - if len(app.Flags) > 0 { - s = append(s, app.FlagSummary()) + if len(cmd.Flags) > 0 { + s = append(s, cmd.FlagSummary()) } - if len(app.Args) > 0 { - s = append(s, app.ArgSummary()) + if len(cmd.Args) > 0 { + s = append(s, cmd.ArgSummary()) } return strings.Join(s, " ") } @@ -162,11 +162,18 @@ func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent "ArgsToTwoColumns": func(a []*ArgModel) [][2]string { rows := [][2]string{} for _, arg := range a { - s := "<" + arg.Name + ">" - if !arg.Required { - s = "[" + s + "]" + if !arg.Hidden { + var s string + if arg.PlaceHolder != "" { + s = arg.PlaceHolder + } else { + s = "<" + arg.Name + ">" + } + if !arg.Required { + s = "[" + s + "]" + } + rows = append(rows, [2]string{s, arg.Help}) } - rows = append(rows, [2]string{s, arg.Help}) } return rows }, @@ -190,6 +197,10 @@ func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent return string(c) }, } + for k, v := range a.usageFuncs { + funcs[k] = v + } + t, err := template.New("usage").Funcs(funcs).Parse(tmpl) if err != nil { return err diff --git a/vendor/modules.txt b/vendor/modules.txt index 3d7ae1b..6bf7c24 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/alecthomas/kingpin v2.2.6+incompatible +# github.com/alecthomas/kingpin v1.3.8-0.20191202215629-0ce3bba646ba github.com/alecthomas/kingpin # github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 github.com/alecthomas/template From 7d31e7dc770260a8b4839bcd92c3253416cbe9ed Mon Sep 17 00:00:00 2001 From: Muhammad Talal Anwar Date: Wed, 8 Jan 2020 14:31:24 +0100 Subject: [PATCH 2/9] Describe all possible metrics manually --- collectors/dispersion.go | 12 +++++++++++- collectors/recon.go | 31 ++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/collectors/dispersion.go b/collectors/dispersion.go index a6d3a23..847cf75 100644 --- a/collectors/dispersion.go +++ b/collectors/dispersion.go @@ -86,7 +86,17 @@ func NewDispersionCollector(pathToExecutable string) *DispersionCollector { // Describe implements the prometheus.Collector interface. func (c *DispersionCollector) Describe(ch chan<- *prometheus.Desc) { - prometheus.DescribeByCollect(c, ch) + ch <- dispersionCntrCopiesExpectedDesc + ch <- dispersionCntrCopiesFoundDesc + ch <- dispersionCntrCopiesMissingDesc + ch <- dispersionCntrOverlappingDesc + + ch <- dispersionObjCopiesExpectedDesc + ch <- dispersionObjCopiesFoundDesc + ch <- dispersionObjCopiesMissingDesc + ch <- dispersionObjOverlappingDesc + + ch <- dispersionTaskErrorDesc } // Collect implements the prometheus.Collector interface. diff --git a/collectors/recon.go b/collectors/recon.go index cfcccc9..df770d2 100644 --- a/collectors/recon.go +++ b/collectors/recon.go @@ -69,7 +69,36 @@ func NewReconCollector(pathToExecutable string) *ReconCollector { // Describe implements the prometheus.Collector interface. func (c *ReconCollector) Describe(ch chan<- *prometheus.Desc) { - prometheus.DescribeByCollect(c, ch) + ch <- reconTaskErrorDesc + + ch <- clusterStorageUsedPercentByDiskDesc + ch <- clusterStorageUsedPercentDesc + ch <- clusterStorageUsedBytesDesc + ch <- clusterStorageFreeBytesDesc + ch <- clusterStorageCapacityBytesDesc + + ch <- clusterMD5AllDesc + ch <- clusterMD5MatchedDesc + ch <- clusterMD5NotMatchedDesc + ch <- clusterMD5ErrorsDesc + + ch <- clusterCntrUpdaterSweepTimeDesc + ch <- clusterObjUpdaterSweepTimeDesc + + ch <- clusterAccReplAgeDesc + ch <- clusterAccReplDurDesc + ch <- clusterCntrReplAgeDesc + ch <- clusterCntrReplDurDesc + ch <- clusterObjReplAgeDesc + ch <- clusterObjReplDurDesc + + ch <- clusterAccQuarantinedDesc + ch <- clusterCntrQuarantinedDesc + ch <- clusterObjQuarantinedDesc + + ch <- clusterDrivesUnmountedDesc + + ch <- clusterDrivesAuditErrsDesc } // Collect implements the prometheus.Collector interface. From 7ae7a21098b4c0928e08b02402ed2ed015766d71 Mon Sep 17 00:00:00 2001 From: Muhammad Talal Anwar Date: Sat, 11 Jan 2020 19:13:26 +0100 Subject: [PATCH 3/9] Add new data structures for collector --- collectors/collector.go | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 collectors/collector.go diff --git a/collectors/collector.go b/collectors/collector.go new file mode 100644 index 0000000..5b85015 --- /dev/null +++ b/collectors/collector.go @@ -0,0 +1,43 @@ +// Copyright 2015 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collectors + +import ( + "strings" + + "github.com/prometheus/client_golang/prometheus" +) + +type typedDesc struct { + desc *prometheus.Desc + valueType prometheus.ValueType +} + +func (d *typedDesc) mustNewConstMetric(value float64, labels ...string) prometheus.Metric { + return prometheus.MustNewConstMetric(d.desc, d.valueType, value, labels...) +} + +func (d *typedDesc) describe(ch chan<- *prometheus.Desc) { + ch <- d.desc +} + +// collectorTask is the interface that a specific collector task must implement. +type collectorTask interface { + describeMetrics(ch chan<- *prometheus.Desc) + collectMetrics(ch chan<- prometheus.Metric, taskExitCodeTypedDesc typedDesc) +} + +func cmdArgsToStr(cmdArgs []string) string { + return strings.Join(cmdArgs, " ") +} From 3c13642e7eb2b3a75350346f8d7b1350ff58a865 Mon Sep 17 00:00:00 2001 From: Muhammad Talal Anwar Date: Sat, 11 Jan 2020 19:13:45 +0100 Subject: [PATCH 4/9] dispersion: update task data structure --- collectors/dispersion.go | 281 ++++++++++---------- collectors/fixtures/dispersion_metrics.prom | 6 +- 2 files changed, 149 insertions(+), 138 deletions(-) diff --git a/collectors/dispersion.go b/collectors/dispersion.go index 847cf75..af2805d 100644 --- a/collectors/dispersion.go +++ b/collectors/dispersion.go @@ -22,162 +22,173 @@ import ( "github.com/sapcc/go-bits/logg" ) -var ( - dispersionCntrCopiesExpectedDesc = prometheus.NewDesc( - "swift_dispersion_container_copies_expected", - "Expected container copies reported by the swift-dispersion-report tool.", - nil, nil, - ) - dispersionCntrCopiesFoundDesc = prometheus.NewDesc( - "swift_dispersion_container_copies_found", - "Found container copies reported by the swift-dispersion-report tool.", - nil, nil, - ) - dispersionCntrCopiesMissingDesc = prometheus.NewDesc( - "swift_dispersion_container_copies_missing", - "Missing container copies reported by the swift-dispersion-report tool.", - nil, nil, - ) - dispersionCntrOverlappingDesc = prometheus.NewDesc( - "swift_dispersion_container_overlapping", - "Expected container copies reported by the swift-dispersion-report tool.", - nil, nil, - ) - - dispersionObjCopiesExpectedDesc = prometheus.NewDesc( - "swift_dispersion_object_copies_expected", - "Expected object copies reported by the swift-dispersion-report tool.", - nil, nil, - ) - dispersionObjCopiesFoundDesc = prometheus.NewDesc( - "swift_dispersion_object_copies_found", - "Found object copies reported by the swift-dispersion-report tool.", - nil, nil, - ) - dispersionObjCopiesMissingDesc = prometheus.NewDesc( - "swift_dispersion_object_copies_missing", - "Missing object copies reported by the swift-dispersion-report tool.", - nil, nil, - ) - dispersionObjOverlappingDesc = prometheus.NewDesc( - "swift_dispersion_object_overlapping", - "Expected object copies reported by the swift-dispersion-report tool.", - nil, nil, - ) - - dispersionTaskErrorDesc = prometheus.NewDesc( - "swift_dispersion_exit_code", - "The exit code for a Swift Dispersion Report query execution.", - []string{"query"}, nil, - ) -) - // DispersionCollector implements the prometheus.Collector interface. type DispersionCollector struct { - pathToExecutable string + taskExitCode typedDesc + dispersionReportDumpTask collectorTask } // NewDispersionCollector creates a new DispersionCollector. func NewDispersionCollector(pathToExecutable string) *DispersionCollector { return &DispersionCollector{ - pathToExecutable: pathToExecutable, + taskExitCode: typedDesc{ + desc: prometheus.NewDesc( + "swift_dispersion_task_exit_code", + "The exit code for a Swift Dispersion Report query execution.", + []string{"query"}, nil), + valueType: prometheus.GaugeValue, + }, + dispersionReportDumpTask: newDispersionReportDumpTask(pathToExecutable), } } // Describe implements the prometheus.Collector interface. func (c *DispersionCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- dispersionCntrCopiesExpectedDesc - ch <- dispersionCntrCopiesFoundDesc - ch <- dispersionCntrCopiesMissingDesc - ch <- dispersionCntrOverlappingDesc - - ch <- dispersionObjCopiesExpectedDesc - ch <- dispersionObjCopiesFoundDesc - ch <- dispersionObjCopiesMissingDesc - ch <- dispersionObjOverlappingDesc - - ch <- dispersionTaskErrorDesc + c.taskExitCode.describe(ch) + c.dispersionReportDumpTask.describeMetrics(ch) } // Collect implements the prometheus.Collector interface. func (c *DispersionCollector) Collect(ch chan<- prometheus.Metric) { - errCount := 0 - query := "--dump-json" - defer func() { - ch <- prometheus.MustNewConstMetric( - dispersionTaskErrorDesc, - prometheus.CounterValue, float64(errCount), - query, - ) - }() + c.dispersionReportDumpTask.collectMetrics(ch, c.taskExitCode) +} + +/////////////////////////////////////////////////////////////////////////////// +// Dispersion collector tasks. + +// dispersionReportDumpTask implements the collector.collectorTask interface. +type dispersionReportDumpTask struct { + pathToDispersionExecutable string + + containerCopiesExpected typedDesc + containerCopiesFound typedDesc + containerCopiesMissing typedDesc + containerOverlapping typedDesc + objectCopiesExpected typedDesc + objectCopiesFound typedDesc + objectCopiesMissing typedDesc + objectOverlapping typedDesc +} - var dispersionReport struct { - Object struct { - Expected int64 `json:"copies_expected"` - Found int64 `json:"copies_found"` - Overlapping int64 `json:"overlapping"` - Missing int64 - } `json:"object"` - Container struct { - Expected int64 `json:"copies_expected"` - Found int64 `json:"copies_found"` - Overlapping int64 `json:"overlapping"` - Missing int64 - } `json:"container"` +func newDispersionReportDumpTask(pathToDispersionExecutable string) collectorTask { + return &dispersionReportDumpTask{ + pathToDispersionExecutable: pathToDispersionExecutable, + containerCopiesExpected: typedDesc{ + desc: prometheus.NewDesc( + "swift_dispersion_container_copies_expected", + "Expected container copies reported by the swift-dispersion-report tool.", + nil, nil), + valueType: prometheus.GaugeValue, + }, + containerCopiesFound: typedDesc{ + desc: prometheus.NewDesc( + "swift_dispersion_container_copies_found", + "Found container copies reported by the swift-dispersion-report tool.", + nil, nil), + valueType: prometheus.GaugeValue, + }, + containerCopiesMissing: typedDesc{ + desc: prometheus.NewDesc( + "swift_dispersion_container_copies_missing", + "Missing container copies reported by the swift-dispersion-report tool.", + nil, nil), + valueType: prometheus.GaugeValue, + }, + containerOverlapping: typedDesc{ + desc: prometheus.NewDesc( + "swift_dispersion_container_overlapping", + "Expected container copies reported by the swift-dispersion-report tool.", + nil, nil), + valueType: prometheus.GaugeValue, + }, + objectCopiesExpected: typedDesc{ + desc: prometheus.NewDesc( + "swift_dispersion_object_copies_expected", + "Expected object copies reported by the swift-dispersion-report tool.", + nil, nil), + valueType: prometheus.GaugeValue, + }, + objectCopiesFound: typedDesc{ + desc: prometheus.NewDesc( + "swift_dispersion_object_copies_found", + "Found object copies reported by the swift-dispersion-report tool.", + nil, nil), + valueType: prometheus.GaugeValue, + }, + objectCopiesMissing: typedDesc{ + desc: prometheus.NewDesc( + "swift_dispersion_object_copies_missing", + "Missing object copies reported by the swift-dispersion-report tool.", + nil, nil), + valueType: prometheus.GaugeValue, + }, + objectOverlapping: typedDesc{ + desc: prometheus.NewDesc( + "swift_dispersion_object_overlapping", + "Expected object copies reported by the swift-dispersion-report tool.", + nil, nil), + valueType: prometheus.GaugeValue, + }, } +} - out, err := exec.Command(c.pathToExecutable, query).Output() - if err != nil { - logg.Error("swift-dispersion-report: %v", err) - errCount++ - return +// dispersionReportDumpTask implements the collector.collectorTask interface. +func (t *dispersionReportDumpTask) describeMetrics(ch chan<- *prometheus.Desc) { + t.containerCopiesExpected.describe(ch) + t.containerCopiesFound.describe(ch) + t.containerCopiesMissing.describe(ch) + t.containerOverlapping.describe(ch) + t.objectCopiesExpected.describe(ch) + t.objectCopiesFound.describe(ch) + t.objectCopiesMissing.describe(ch) + t.objectOverlapping.describe(ch) +} + +// dispersionReportDumpTask implements the collector.collectorTask interface. +func (t *dispersionReportDumpTask) collectMetrics(ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) { + exitCode := 0 + cmdArg := "--dump-json" + out, err := exec.Command(t.pathToDispersionExecutable, cmdArg).Output() + if err == nil { + var data struct { + Object struct { + Expected int64 `json:"copies_expected"` + Found int64 `json:"copies_found"` + Overlapping int64 `json:"overlapping"` + Missing int64 + } `json:"object"` + Container struct { + Expected int64 `json:"copies_expected"` + Found int64 `json:"copies_found"` + Overlapping int64 `json:"overlapping"` + Missing int64 + } `json:"container"` + } + err = json.Unmarshal(out, &data) + if err == nil { + cntr := data.Container + if cntr.Expected > 0 && cntr.Found > 0 { + cntr.Missing = cntr.Expected - cntr.Found + } + ch <- t.containerCopiesExpected.mustNewConstMetric(float64(cntr.Expected)) + ch <- t.containerCopiesFound.mustNewConstMetric(float64(cntr.Found)) + ch <- t.containerCopiesMissing.mustNewConstMetric(float64(cntr.Missing)) + ch <- t.containerOverlapping.mustNewConstMetric(float64(cntr.Overlapping)) + + obj := data.Object + if obj.Expected > 0 && obj.Found > 0 { + obj.Missing = obj.Expected - obj.Found + } + ch <- t.objectCopiesExpected.mustNewConstMetric(float64(obj.Expected)) + ch <- t.objectCopiesFound.mustNewConstMetric(float64(obj.Found)) + ch <- t.objectCopiesMissing.mustNewConstMetric(float64(obj.Missing)) + ch <- t.objectOverlapping.mustNewConstMetric(float64(obj.Overlapping)) + } } - err = json.Unmarshal(out, &dispersionReport) if err != nil { - logg.Error("swift-dispersion-report: %v", err) - errCount++ - return - } - - cntr := dispersionReport.Container - if cntr.Expected > 0 && cntr.Found > 0 { - cntr.Missing = cntr.Expected - cntr.Found + exitCode = 1 + logg.Error("swift dispersion: %s: %s", cmdArg, err.Error()) } - ch <- prometheus.MustNewConstMetric( - dispersionCntrCopiesExpectedDesc, - prometheus.GaugeValue, float64(cntr.Expected), - ) - ch <- prometheus.MustNewConstMetric( - dispersionCntrCopiesFoundDesc, - prometheus.GaugeValue, float64(cntr.Found), - ) - ch <- prometheus.MustNewConstMetric( - dispersionCntrCopiesMissingDesc, - prometheus.GaugeValue, float64(cntr.Missing), - ) - ch <- prometheus.MustNewConstMetric( - dispersionCntrOverlappingDesc, - prometheus.GaugeValue, float64(cntr.Overlapping), - ) - obj := dispersionReport.Object - if obj.Expected > 0 && obj.Found > 0 { - obj.Missing = obj.Expected - obj.Found - } - ch <- prometheus.MustNewConstMetric( - dispersionObjCopiesExpectedDesc, - prometheus.GaugeValue, float64(obj.Expected), - ) - ch <- prometheus.MustNewConstMetric( - dispersionObjCopiesFoundDesc, - prometheus.GaugeValue, float64(obj.Found), - ) - ch <- prometheus.MustNewConstMetric( - dispersionObjCopiesMissingDesc, - prometheus.GaugeValue, float64(obj.Missing), - ) - ch <- prometheus.MustNewConstMetric( - dispersionObjOverlappingDesc, - prometheus.GaugeValue, float64(obj.Overlapping), - ) + ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), cmdArg) } diff --git a/collectors/fixtures/dispersion_metrics.prom b/collectors/fixtures/dispersion_metrics.prom index 79d37cf..8ba0a12 100644 --- a/collectors/fixtures/dispersion_metrics.prom +++ b/collectors/fixtures/dispersion_metrics.prom @@ -10,9 +10,6 @@ swift_dispersion_container_copies_missing 0 # HELP swift_dispersion_container_overlapping Expected container copies reported by the swift-dispersion-report tool. # TYPE swift_dispersion_container_overlapping gauge swift_dispersion_container_overlapping 0 -# HELP swift_dispersion_exit_code The exit code for a Swift Dispersion Report query execution. -# TYPE swift_dispersion_exit_code counter -swift_dispersion_exit_code{query="--dump-json"} 0 # HELP swift_dispersion_object_copies_expected Expected object copies reported by the swift-dispersion-report tool. # TYPE swift_dispersion_object_copies_expected gauge swift_dispersion_object_copies_expected 1965 @@ -25,3 +22,6 @@ swift_dispersion_object_copies_missing 0 # HELP swift_dispersion_object_overlapping Expected object copies reported by the swift-dispersion-report tool. # TYPE swift_dispersion_object_overlapping gauge swift_dispersion_object_overlapping 0 +# HELP swift_dispersion_task_exit_code The exit code for a Swift Dispersion Report query execution. +# TYPE swift_dispersion_task_exit_code gauge +swift_dispersion_task_exit_code{failed_query="",task="reportDump"} 0 From a392755d1c7c5935b3b2e2ed937881902c1f8026 Mon Sep 17 00:00:00 2001 From: Muhammad Talal Anwar Date: Sat, 11 Jan 2020 19:14:11 +0100 Subject: [PATCH 5/9] recon: update task data structure --- collectors/fixtures/recon_metrics.prom | 25 +- collectors/recon.go | 939 +++++++++++++------------ 2 files changed, 504 insertions(+), 460 deletions(-) diff --git a/collectors/fixtures/recon_metrics.prom b/collectors/fixtures/recon_metrics.prom index 3ff935c..44bb896 100644 --- a/collectors/fixtures/recon_metrics.prom +++ b/collectors/fixtures/recon_metrics.prom @@ -78,11 +78,11 @@ swift_cluster_storage_free_bytes{storage_ip="10.0.0.2"} 7.8783182286848e+13 # TYPE swift_cluster_storage_used_bytes gauge swift_cluster_storage_used_bytes{storage_ip="10.0.0.1"} 5.217681207296e+12 swift_cluster_storage_used_bytes{storage_ip="10.0.0.2"} 5.203322146816e+12 -# HELP swift_cluster_storage_used_percent Percentage of storage used as reported by the swift-recon tool. +# HELP swift_cluster_storage_used_percent Fractional usage as reported by the swift-recon tool. # TYPE swift_cluster_storage_used_percent gauge swift_cluster_storage_used_percent{storage_ip="10.0.0.1"} 0.06212523360127625 swift_cluster_storage_used_percent{storage_ip="10.0.0.2"} 0.06195426493700304 -# HELP swift_cluster_storage_used_percent_by_disk Percentage of storage used by a disk as reported by the swift-recon tool. +# HELP swift_cluster_storage_used_percent_by_disk Fractional usage of a disk as reported by the swift-recon tool. # TYPE swift_cluster_storage_used_percent_by_disk gauge swift_cluster_storage_used_percent_by_disk{disk="sdb01",storage_ip="10.0.0.1"} 0.06323726985355019 swift_cluster_storage_used_percent_by_disk{disk="sdb01",storage_ip="10.0.0.2"} 0.05993283299508797 @@ -112,15 +112,12 @@ swift_cluster_storage_used_percent_by_disk{disk="sdb13",storage_ip="10.0.0.1"} 0 swift_cluster_storage_used_percent_by_disk{disk="sdb13",storage_ip="10.0.0.2"} 0.06277274773472794 swift_cluster_storage_used_percent_by_disk{disk="sdb14",storage_ip="10.0.0.1"} 0.0590386342491059 swift_cluster_storage_used_percent_by_disk{disk="sdb14",storage_ip="10.0.0.2"} 0.06518201289089147 -# HELP swift_recon_exit_code The exit code for a Swift Recon query execution. -# TYPE swift_recon_exit_code counter -swift_recon_exit_code{query="--diskusage --verbose"} 0 -swift_recon_exit_code{query="--driveaudit --verbose"} 0 -swift_recon_exit_code{query="--md5"} 0 -swift_recon_exit_code{query="--quarantined --verbose"} 0 -swift_recon_exit_code{query="--unmounted --verbose"} 0 -swift_recon_exit_code{query="account --replication --verbose"} 0 -swift_recon_exit_code{query="container --replication --verbose"} 0 -swift_recon_exit_code{query="container --updater --verbose"} 0 -swift_recon_exit_code{query="object --replication --verbose"} 0 -swift_recon_exit_code{query="object --updater --verbose"} 0 +# HELP swift_recon_task_exit_code The exit code for a Swift Recon query execution. +# TYPE swift_recon_task_exit_code gauge +swift_recon_task_exit_code{failed_query="",task="diskUsage"} 0 +swift_recon_task_exit_code{failed_query="",task="driveAudit"} 0 +swift_recon_task_exit_code{failed_query="",task="md5"} 0 +swift_recon_task_exit_code{failed_query="",task="quarantined"} 0 +swift_recon_task_exit_code{failed_query="",task="replication"} 0 +swift_recon_task_exit_code{failed_query="",task="unmounted"} 0 +swift_recon_task_exit_code{failed_query="",task="updaterSweep"} 0 diff --git a/collectors/recon.go b/collectors/recon.go index df770d2..8e54916 100644 --- a/collectors/recon.go +++ b/collectors/recon.go @@ -17,7 +17,7 @@ package collectors import ( "bytes" "encoding/json" - "fmt" + "errors" "os/exec" "regexp" "strconv" @@ -28,573 +28,620 @@ import ( "github.com/sapcc/go-bits/logg" ) -var ( - reconTaskErrorDesc = prometheus.NewDesc( - "swift_recon_exit_code", - "The exit code for a Swift Recon query execution.", - []string{"query"}, nil, - ) - - sendReconErrorCount = func(ch chan<- prometheus.Metric, cmdArgs []string, count int) { - query := strings.Join(cmdArgs, " ") - ch <- prometheus.MustNewConstMetric( - reconTaskErrorDesc, - prometheus.CounterValue, float64(count), - query, - ) - } -) - // ReconCollector implements the prometheus.Collector interface. type ReconCollector struct { - pathToExecutable string - tasks map[string]func(string, string, chan<- prometheus.Metric) + taskExitCode typedDesc + tasks []collectorTask } // NewReconCollector creates a new ReconCollector. func NewReconCollector(pathToExecutable string) *ReconCollector { return &ReconCollector{ - pathToExecutable: pathToExecutable, - tasks: map[string]func(string, string, chan<- prometheus.Metric){ - "diskUsage": reconDiskUsageTask, - "driveAudit": reconDriveAuditTask, - "md5": reconMD5Task, - "quarantined": reconQuarantinedTask, - "replication": reconReplicationTask, - "unmounted": reconUnmountedTask, - "updaterSweep": reconUpdaterSweepTask, + taskExitCode: typedDesc{ + desc: prometheus.NewDesc("swift_recon_task_exit_code", + "The exit code for a Swift Recon query execution.", + []string{"query"}, nil), + valueType: prometheus.GaugeValue, + }, + tasks: []collectorTask{ + newReconDiskUsageTask(pathToExecutable), + newReconDriveAuditTask(pathToExecutable), + newReconMD5Task(pathToExecutable), + newReconQuarantinedTask(pathToExecutable), + newReconReplicationTask(pathToExecutable), + newReconUnmountedTask(pathToExecutable), + newReconUpdaterSweepTask(pathToExecutable), }, } } // Describe implements the prometheus.Collector interface. func (c *ReconCollector) Describe(ch chan<- *prometheus.Desc) { - ch <- reconTaskErrorDesc - - ch <- clusterStorageUsedPercentByDiskDesc - ch <- clusterStorageUsedPercentDesc - ch <- clusterStorageUsedBytesDesc - ch <- clusterStorageFreeBytesDesc - ch <- clusterStorageCapacityBytesDesc - - ch <- clusterMD5AllDesc - ch <- clusterMD5MatchedDesc - ch <- clusterMD5NotMatchedDesc - ch <- clusterMD5ErrorsDesc - - ch <- clusterCntrUpdaterSweepTimeDesc - ch <- clusterObjUpdaterSweepTimeDesc - - ch <- clusterAccReplAgeDesc - ch <- clusterAccReplDurDesc - ch <- clusterCntrReplAgeDesc - ch <- clusterCntrReplDurDesc - ch <- clusterObjReplAgeDesc - ch <- clusterObjReplDurDesc + c.taskExitCode.describe(ch) - ch <- clusterAccQuarantinedDesc - ch <- clusterCntrQuarantinedDesc - ch <- clusterObjQuarantinedDesc - - ch <- clusterDrivesUnmountedDesc - - ch <- clusterDrivesAuditErrsDesc + for _, t := range c.tasks { + t.describeMetrics(ch) + } } // Collect implements the prometheus.Collector interface. func (c *ReconCollector) Collect(ch chan<- prometheus.Metric) { wg := sync.WaitGroup{} wg.Add(len(c.tasks)) - for name, task := range c.tasks { - go func(name string, task func(string, string, chan<- prometheus.Metric)) { - task(name, c.pathToExecutable, ch) + for _, t := range c.tasks { + go func(t collectorTask) { + t.collectMetrics(ch, c.taskExitCode) wg.Done() - }(name, task) + }(t) } wg.Wait() } -var ( - clusterStorageUsedPercentByDiskDesc = prometheus.NewDesc( - "swift_cluster_storage_used_percent_by_disk", - "Percentage of storage used by a disk as reported by the swift-recon tool.", - []string{"storage_ip", "disk"}, nil, - ) - clusterStorageUsedPercentDesc = prometheus.NewDesc( - "swift_cluster_storage_used_percent", - "Percentage of storage used as reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - clusterStorageUsedBytesDesc = prometheus.NewDesc( - "swift_cluster_storage_used_bytes", - "Used storage bytes as reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - clusterStorageFreeBytesDesc = prometheus.NewDesc( - "swift_cluster_storage_free_bytes", - "Free storage bytes as reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - clusterStorageCapacityBytesDesc = prometheus.NewDesc( - "swift_cluster_storage_capacity_bytes", - "Capacity storage bytes as reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) -) +/////////////////////////////////////////////////////////////////////////////// +// Recon collector tasks. + +// reconDiskUsageTask implements the collector.collectorTask interface. +type reconDiskUsageTask struct { + pathToReconExecutable string + specialCharRx *regexp.Regexp + + capacityBytes typedDesc + freeBytes typedDesc + usedBytes typedDesc + fractionalUsage typedDesc + fractionalUsageByDisk typedDesc +} -var specialCharRx = regexp.MustCompile(`[^a-zA-Z0-9]+`) +func newReconDiskUsageTask(pathToReconExecutable string) collectorTask { + return &reconDiskUsageTask{ + pathToReconExecutable: pathToReconExecutable, + specialCharRx: regexp.MustCompile(`[^a-zA-Z0-9]+`), + capacityBytes: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_storage_capacity_bytes", + "Capacity storage bytes as reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + freeBytes: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_storage_free_bytes", + "Free storage bytes as reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + usedBytes: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_storage_used_bytes", + "Used storage bytes as reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + fractionalUsage: typedDesc{ + // In order to be consistent with the legacy system, the metric + // name uses the word percent instead of fractional. + desc: prometheus.NewDesc("swift_cluster_storage_used_percent", + "Fractional usage as reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + fractionalUsageByDisk: typedDesc{ + // In order to be consistent with the legacy system, the metric + // name uses the word percent instead of fractional. + desc: prometheus.NewDesc("swift_cluster_storage_used_percent_by_disk", + "Fractional usage of a disk as reported by the swift-recon tool.", + []string{"storage_ip", "disk"}, nil), + valueType: prometheus.GaugeValue, + }, + } +} + +// reconDiskUsageTask implements the collector.collectorTask interface. +func (t *reconDiskUsageTask) describeMetrics(ch chan<- *prometheus.Desc) { + t.capacityBytes.describe(ch) + t.freeBytes.describe(ch) + t.usedBytes.describe(ch) + t.fractionalUsage.describe(ch) + t.fractionalUsageByDisk.describe(ch) +} -func reconDiskUsageTask(taskName, pathToReconExecutable string, ch chan<- prometheus.Metric) { - errCount := 0 +// reconDiskUsageTask implements the collector.collectorTask interface. +func (t *reconDiskUsageTask) collectMetrics(ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) { + exitCode := 0 cmdArgs := []string{"--diskusage", "--verbose"} - defer func() { - sendReconErrorCount(ch, cmdArgs, errCount) - }() + outputPerHost, err := getSwiftReconOutputPerHost(t.pathToReconExecutable, cmdArgs...) + if err == nil { + for hostname, dataBytes := range outputPerHost { + var disksData []struct { + Device string `json:"device"` + Avail int64 `json:"avail"` + Mounted bool `json:"mounted"` + Used int64 `json:"used"` + Size int64 `json:"size"` + } + err := json.Unmarshal(dataBytes, &disksData) + if err != nil { + exitCode = 1 + logg.Error("swift dispersion: %s: %s: %s", cmdArgsToStr(cmdArgs), hostname, err.Error()) + continue // to next host + } - result, err := getSwiftReconOutputPerHost(pathToReconExecutable, cmdArgs...) - if err != nil { - logg.Error("recon collector: %s: %v", taskName, err) - errCount++ - return - } + var totalFree, totalUsed, totalSize int64 + for _, disk := range disksData { + if !(disk.Mounted) { + continue // to next disk + } - for hostname, dataBytes := range result { - var disksData []struct { - Device string `json:"device"` - Avail int64 `json:"avail"` - Mounted bool `json:"mounted"` - Used int64 `json:"used"` - Size int64 `json:"size"` - } - err = json.Unmarshal(dataBytes, &disksData) - if err != nil { - logg.Error("recon collector: %s: %s: %v", taskName, hostname, err) - errCount++ - continue - } + totalFree += disk.Avail + totalUsed += disk.Used + totalSize += disk.Size - var totalFree, totalUsed, totalSize int64 - for _, disk := range disksData { - if !(disk.Mounted) { - continue + // submit metrics by disk (only fractional usage, which is the + // most useful for alerting) + device := t.specialCharRx.ReplaceAllLiteralString(disk.Device, "") + diskUsageRatio := float64(disk.Used) / float64(disk.Size) + ch <- t.fractionalUsageByDisk.mustNewConstMetric(diskUsageRatio, hostname, device) } - totalFree += disk.Avail - totalUsed += disk.Used - totalSize += disk.Size - - // submit metrics by disk (only used percent here, which is the - // most useful for alerting) - device := specialCharRx.ReplaceAllLiteralString(disk.Device, "") - ch <- prometheus.MustNewConstMetric( - clusterStorageUsedPercentByDiskDesc, - prometheus.GaugeValue, float64(disk.Used)/float64(disk.Size), - hostname, device, - ) - } - - usedRatio := float64(totalUsed) / float64(totalSize) - if totalSize == 0 { - usedRatio = 1.0 + usageRatio := float64(totalUsed) / float64(totalSize) + if totalSize == 0 { + usageRatio = 1.0 + } + ch <- t.fractionalUsage.mustNewConstMetric(usageRatio, hostname) + ch <- t.usedBytes.mustNewConstMetric(float64(totalUsed), hostname) + ch <- t.freeBytes.mustNewConstMetric(float64(totalFree), hostname) + ch <- t.capacityBytes.mustNewConstMetric(float64(totalSize), hostname) } - ch <- prometheus.MustNewConstMetric( - clusterStorageUsedPercentDesc, - prometheus.GaugeValue, usedRatio, - hostname, - ) - ch <- prometheus.MustNewConstMetric( - clusterStorageUsedBytesDesc, - prometheus.GaugeValue, float64(totalUsed), - hostname, - ) - ch <- prometheus.MustNewConstMetric( - clusterStorageFreeBytesDesc, - prometheus.GaugeValue, float64(totalFree), - hostname, - ) - ch <- prometheus.MustNewConstMetric( - clusterStorageCapacityBytesDesc, - prometheus.GaugeValue, float64(totalSize), - hostname, - ) + } else { + exitCode = 1 + logg.Error("swift dispersion: %s: %s", cmdArgsToStr(cmdArgs), err.Error()) } + + ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), cmdArgsToStr(cmdArgs)) } -var ( - clusterMD5AllDesc = prometheus.NewDesc( - "swift_cluster_md5_all", - "Sum of matched-, not matched hosts, and errors encountered while check hosts for md5sum(s) as reported by the swift-recon tool.", - []string{"kind"}, nil, - ) - clusterMD5MatchedDesc = prometheus.NewDesc( - "swift_cluster_md5_matched", - "Matched hosts for md5sum(s) reported by the swift-recon tool.", - []string{"kind"}, nil, - ) - clusterMD5NotMatchedDesc = prometheus.NewDesc( - "swift_cluster_md5_not_matched", - "Not matched hosts for md5sum(s) reported by the swift-recon tool.", - []string{"kind"}, nil, - ) - clusterMD5ErrorsDesc = prometheus.NewDesc( - "swift_cluster_md5_errors", - "Errors encountered while checking hosts for md5sum(s) as reported by the swift-recon tool.", - []string{"kind"}, nil, - ) -) +// reconMD5Task implements the collector.collectorTask interface. +type reconMD5Task struct { + pathToReconExecutable string + md5OutputRx *regexp.Regexp -var reconMD5Rx = regexp.MustCompile( - `(?m)^.* Checking ([\.a-zA-Z0-9_]+) md5sum(?:s)?\s*([0-9]+)/([0-9]+) hosts matched, ([0-9]+) error.*$`) + all typedDesc + errors typedDesc + matched typedDesc + notMatched typedDesc +} + +func newReconMD5Task(pathToReconExecutable string) collectorTask { + return &reconMD5Task{ + pathToReconExecutable: pathToReconExecutable, + md5OutputRx: regexp.MustCompile( + `(?m)^.* Checking ([\.a-zA-Z0-9_]+) md5sum(?:s)?\s*([0-9]+)/([0-9]+) hosts matched, ([0-9]+) error.*$`), + all: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_md5_all", + "Sum of matched-, not matched hosts, and errors encountered while check hosts for md5sum(s) as reported by the swift-recon tool.", + []string{"kind"}, nil), + valueType: prometheus.GaugeValue, + }, + errors: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_md5_errors", + "Errors encountered while checking hosts for md5sum(s) as reported by the swift-recon tool.", + []string{"kind"}, nil), + valueType: prometheus.GaugeValue, + }, + matched: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_md5_matched", + "Matched hosts for md5sum(s) reported by the swift-recon tool.", + []string{"kind"}, nil), + valueType: prometheus.GaugeValue, + }, + notMatched: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_md5_not_matched", + "Not matched hosts for md5sum(s) reported by the swift-recon tool.", + []string{"kind"}, nil), + valueType: prometheus.GaugeValue, + }, + } +} -func reconMD5Task(taskName, pathToReconExecutable string, ch chan<- prometheus.Metric) { - errCount := 0 - cmdArgs := []string{"--md5"} - defer func() { - sendReconErrorCount(ch, cmdArgs, errCount) - }() +// reconMD5Task implements the collector.collectorTask interface. +func (t *reconMD5Task) describeMetrics(ch chan<- *prometheus.Desc) { + t.all.describe(ch) + t.errors.describe(ch) + t.matched.describe(ch) + t.notMatched.describe(ch) +} - cmd := exec.Command(pathToReconExecutable, cmdArgs...) - out, err := cmd.Output() +// reconMD5Task implements the collector.collectorTask interface. +func (t *reconMD5Task) collectMetrics(ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) { + exitCode := 0 + cmdArg := "--md5" + out, err := exec.Command(t.pathToReconExecutable, cmdArg).Output() + if err == nil { + matchList := t.md5OutputRx.FindAllSubmatch(out, -1) + if len(matchList) > 0 { + for _, match := range matchList { + var totalHosts, errsEncountered float64 + matchedHosts, err := strconv.ParseFloat(string(match[2]), 64) + if err == nil { + totalHosts, err = strconv.ParseFloat(string(match[3]), 64) + if err == nil { + errsEncountered, err = strconv.ParseFloat(string(match[4]), 64) + if err == nil { + kind := strings.ReplaceAll(string(match[1]), ".", "") + notMatchedHosts := totalHosts - matchedHosts + all := matchedHosts + notMatchedHosts + errsEncountered + ch <- t.all.mustNewConstMetric(all, kind) + ch <- t.errors.mustNewConstMetric(errsEncountered, kind) + ch <- t.matched.mustNewConstMetric(matchedHosts, kind) + ch <- t.notMatched.mustNewConstMetric(notMatchedHosts, kind) + } + } + } + if err != nil { + exitCode = 1 + logg.Error("swift dispersion: %s: %s", cmdArg, err.Error()) + } + } + } else { + err = errors.New("command did not return any usable output") + } + } if err != nil { - logg.Error("recon collector: %s: %v", taskName, err) - errCount++ - return + exitCode = 1 + logg.Error("swift dispersion: %s: %s", cmdArg, err.Error()) } - matchList := reconMD5Rx.FindAllSubmatch(out, -1) - if len(matchList) == 0 { - logg.Error("recon collector: %s: command %q did not return any usable output", taskName, cmd) - errCount++ - return - } + ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), cmdArg) +} - for _, match := range matchList { - kind := strings.ReplaceAll(string(match[1]), ".", "") - matchedHosts, err := strconv.ParseFloat(string(match[2]), 64) - if err != nil { - logg.Error("recon collector: %s: %v", taskName, err) - errCount++ - continue - } - totalHosts, err := strconv.ParseFloat(string(match[3]), 64) - if err != nil { - logg.Error("recon collector: %s: %v", taskName, err) - errCount++ - continue - } - notMatchedHosts := totalHosts - matchedHosts - errsEncountered, err := strconv.ParseFloat(string(match[4]), 64) - if err != nil { - logg.Error("recon collector: %s: %v", taskName, err) - errCount++ - continue - } +// reconUpdaterSweepTask implements the collector.collectorTask interface. +type reconUpdaterSweepTask struct { + pathToReconExecutable string - ch <- prometheus.MustNewConstMetric( - clusterMD5AllDesc, - prometheus.GaugeValue, matchedHosts+notMatchedHosts+errsEncountered, - kind, - ) - ch <- prometheus.MustNewConstMetric( - clusterMD5MatchedDesc, - prometheus.GaugeValue, matchedHosts, - kind, - ) - ch <- prometheus.MustNewConstMetric( - clusterMD5NotMatchedDesc, - prometheus.GaugeValue, notMatchedHosts, - kind, - ) - ch <- prometheus.MustNewConstMetric( - clusterMD5ErrorsDesc, - prometheus.GaugeValue, errsEncountered, - kind, - ) + containerTime typedDesc + objectTime typedDesc +} + +func newReconUpdaterSweepTask(pathToReconExecutable string) collectorTask { + return &reconUpdaterSweepTask{ + pathToReconExecutable: pathToReconExecutable, + containerTime: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_containers_updater_sweep_time", + "Container updater sweep time reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + objectTime: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_objects_updater_sweep_time", + "Object updater sweep time reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, } } -var ( - clusterCntrUpdaterSweepTimeDesc = prometheus.NewDesc( - "swift_cluster_containers_updater_sweep_time", - "Container updater sweep time reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - clusterObjUpdaterSweepTimeDesc = prometheus.NewDesc( - "swift_cluster_objects_updater_sweep_time", - "Object updater sweep time reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) -) +// reconUpdaterSweepTask implements the collector.collectorTask interface. +func (t *reconUpdaterSweepTask) describeMetrics(ch chan<- *prometheus.Desc) { + t.containerTime.describe(ch) + t.objectTime.describe(ch) +} -func reconUpdaterSweepTask(taskName, pathToReconExecutable string, ch chan<- prometheus.Metric) { +// reconUpdaterSweepTask implements the collector.collectorTask interface. +func (t *reconUpdaterSweepTask) collectMetrics(ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) { serverTypes := []string{"container", "object"} for _, server := range serverTypes { - errCount := 0 + exitCode := 0 cmdArgs := []string{server, "--updater", "--verbose"} - - result, err := getSwiftReconOutputPerHost(pathToReconExecutable, cmdArgs...) + outputPerHost, err := getSwiftReconOutputPerHost(t.pathToReconExecutable, cmdArgs...) if err == nil { - for hostname, dataBytes := range result { + for hostname, dataBytes := range outputPerHost { var data struct { ContainerUpdaterSweepTime float64 `json:"container_updater_sweep"` ObjectUpdaterSweepTime float64 `json:"object_updater_sweep"` } err := json.Unmarshal(dataBytes, &data) if err != nil { - logg.Error("recon collector: %s: %s: %s: %v", taskName, server, hostname, err) - errCount++ - continue + exitCode = 1 + logg.Error("swift dispersion: %s: %s: %s", cmdArgsToStr(cmdArgs), hostname, err.Error()) + continue // to next host } val := data.ContainerUpdaterSweepTime - desc := clusterCntrUpdaterSweepTimeDesc + desc := t.containerTime if server == "object" { val = data.ObjectUpdaterSweepTime - desc = clusterObjUpdaterSweepTimeDesc + desc = t.objectTime } - ch <- prometheus.MustNewConstMetric( - desc, - prometheus.GaugeValue, val, - hostname, - ) + ch <- desc.mustNewConstMetric(val, hostname) } } else { - logg.Error("recon collector: %s: %s: %v", taskName, server, err) - errCount++ + exitCode = 1 + logg.Error("swift recon: %s: %s", cmdArgsToStr(cmdArgs), err.Error()) } - sendReconErrorCount(ch, cmdArgs, errCount) + ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), cmdArgsToStr(cmdArgs)) } } -var ( - clusterAccReplAgeDesc = prometheus.NewDesc( - "swift_cluster_accounts_replication_age", - "Account replication age reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - clusterAccReplDurDesc = prometheus.NewDesc( - "swift_cluster_accounts_replication_duration", - "Account replication duration reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - - clusterCntrReplAgeDesc = prometheus.NewDesc( - "swift_cluster_containers_replication_age", - "Container replication age reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - clusterCntrReplDurDesc = prometheus.NewDesc( - "swift_cluster_containers_replication_duration", - "Container replication duration reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - - clusterObjReplAgeDesc = prometheus.NewDesc( - "swift_cluster_objects_replication_age", - "Object replication age reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - clusterObjReplDurDesc = prometheus.NewDesc( - "swift_cluster_objects_replication_duration", - "Object replication duration reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) -) +// reconReplicationTask implements the collector.collectorTask interface. +type reconReplicationTask struct { + pathToReconExecutable string + + accountReplicationAge typedDesc + accountReplicationDuration typedDesc + containerReplicationAge typedDesc + containerReplicationDuration typedDesc + objectReplicationAge typedDesc + objectReplicationDuration typedDesc +} + +func newReconReplicationTask(pathToReconExecutable string) collectorTask { + return &reconReplicationTask{ + pathToReconExecutable: pathToReconExecutable, + accountReplicationAge: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_accounts_replication_age", + "Account replication age reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + accountReplicationDuration: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_accounts_replication_duration", + "Account replication duration reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + containerReplicationAge: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_containers_replication_age", + "Container replication age reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + containerReplicationDuration: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_containers_replication_duration", + "Container replication duration reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + objectReplicationAge: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_objects_replication_age", + "Object replication age reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + objectReplicationDuration: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_objects_replication_duration", + "Object replication duration reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + } +} -func reconReplicationTask(taskName, pathToReconExecutable string, ch chan<- prometheus.Metric) { +// reconReplicationTask implements the collector.collectorTask interface. +func (t *reconReplicationTask) describeMetrics(ch chan<- *prometheus.Desc) { + t.accountReplicationAge.describe(ch) + t.accountReplicationDuration.describe(ch) + t.containerReplicationAge.describe(ch) + t.containerReplicationDuration.describe(ch) + t.objectReplicationAge.describe(ch) + t.objectReplicationDuration.describe(ch) +} + +// reconReplicationTask implements the collector.collectorTask interface. +func (t *reconReplicationTask) collectMetrics(ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) { serverTypes := []string{"account", "container", "object"} for _, server := range serverTypes { - errCount := 0 + exitCode := 0 cmdArgs := []string{server, "--replication", "--verbose"} - var ageDesc, durDesc *prometheus.Desc + var ageTypedDesc, durTypedDesc typedDesc switch server { case "account": - ageDesc = clusterAccReplAgeDesc - durDesc = clusterAccReplDurDesc + ageTypedDesc = t.accountReplicationAge + durTypedDesc = t.accountReplicationDuration case "container": - ageDesc = clusterCntrReplAgeDesc - durDesc = clusterCntrReplDurDesc + ageTypedDesc = t.containerReplicationAge + durTypedDesc = t.containerReplicationDuration case "object": - ageDesc = clusterObjReplAgeDesc - durDesc = clusterObjReplDurDesc + ageTypedDesc = t.objectReplicationAge + durTypedDesc = t.objectReplicationDuration } - result, err := getSwiftReconOutputPerHost(pathToReconExecutable, cmdArgs...) + outputPerHost, err := getSwiftReconOutputPerHost(t.pathToReconExecutable, cmdArgs...) if err == nil { - for hostname, dataBytes := range result { + for hostname, dataBytes := range outputPerHost { var data struct { ReplicationLast float64 `json:"replication_last"` ReplicationTime float64 `json:"replication_time"` } err := json.Unmarshal(dataBytes, &data) if err != nil { - logg.Error("recon collector: %s: %s: %s: %v", taskName, server, hostname, err) - errCount++ - continue + exitCode = 1 + logg.Error("swift dispersion: %s: %s: %s", cmdArgsToStr(cmdArgs), hostname, err.Error()) + continue // to next host } - ch <- prometheus.MustNewConstMetric( - ageDesc, - prometheus.GaugeValue, data.ReplicationLast, - hostname, - ) - ch <- prometheus.MustNewConstMetric( - durDesc, - prometheus.GaugeValue, data.ReplicationTime, - hostname, - ) + ch <- ageTypedDesc.mustNewConstMetric(data.ReplicationLast, hostname) + ch <- durTypedDesc.mustNewConstMetric(data.ReplicationTime, hostname) } } else { - logg.Error("recon collector: %s: %s: %v", taskName, server, err) - errCount++ + exitCode = 1 + logg.Error("swift recon: %s: %s", cmdArgsToStr(cmdArgs), err.Error()) } - sendReconErrorCount(ch, cmdArgs, errCount) + ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), cmdArgsToStr(cmdArgs)) } } -var ( - clusterAccQuarantinedDesc = prometheus.NewDesc( - "swift_cluster_accounts_quarantined", - "Quarantined accounts reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - clusterCntrQuarantinedDesc = prometheus.NewDesc( - "swift_cluster_containers_quarantined", - "Quarantined containers reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) - clusterObjQuarantinedDesc = prometheus.NewDesc( - "swift_cluster_objects_quarantined", - "Quarantined objects reported by the swift-recon tool.", - []string{"storage_ip"}, nil, - ) -) +// reconQuarantinedTask implements the collector.collectorTask interface. +type reconQuarantinedTask struct { + pathToReconExecutable string -func reconQuarantinedTask(taskName, pathToReconExecutable string, ch chan<- prometheus.Metric) { - errCount := 0 - cmdArgs := []string{"--quarantined", "--verbose"} - defer func() { - sendReconErrorCount(ch, cmdArgs, errCount) - }() + accounts typedDesc + containers typedDesc + objects typedDesc +} - result, err := getSwiftReconOutputPerHost(pathToReconExecutable, cmdArgs...) - if err != nil { - logg.Error("recon collector: %s: %v", taskName, err) - errCount++ - return +func newReconQuarantinedTask(pathToReconExecutable string) collectorTask { + return &reconQuarantinedTask{ + pathToReconExecutable: pathToReconExecutable, + accounts: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_accounts_quarantined", + "Quarantined accounts reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + containers: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_containers_quarantined", + "Quarantined containers reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, + objects: typedDesc{ + desc: prometheus.NewDesc("swift_cluster_objects_quarantined", + "Quarantined objects reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, } +} - for hostname, dataBytes := range result { - var data struct { - Objects int64 `json:"objects"` - Accounts int64 `json:"accounts"` - Containers int64 `json:"containers"` - } - err := json.Unmarshal(dataBytes, &data) - if err != nil { - logg.Error("recon collector: %s: %s: %v", taskName, hostname, err) - errCount++ - continue +// reconQuarantinedTask implements the collector.collectorTask interface. +func (t *reconQuarantinedTask) describeMetrics(ch chan<- *prometheus.Desc) { + t.accounts.describe(ch) + t.containers.describe(ch) + t.objects.describe(ch) +} + +// reconQuarantinedTask implements the collector.collectorTask interface. +func (t *reconQuarantinedTask) collectMetrics(ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) { + exitCode := 0 + cmdArgs := []string{"--quarantined", "--verbose"} + outputPerHost, err := getSwiftReconOutputPerHost(t.pathToReconExecutable, cmdArgs...) + if err == nil { + for hostname, dataBytes := range outputPerHost { + var data struct { + Objects int64 `json:"objects"` + Accounts int64 `json:"accounts"` + Containers int64 `json:"containers"` + } + err := json.Unmarshal(dataBytes, &data) + if err != nil { + exitCode = 1 + logg.Error("swift dispersion: %s: %s: %s", cmdArgsToStr(cmdArgs), hostname, err.Error()) + continue // to next host + } + + ch <- t.accounts.mustNewConstMetric(float64(data.Accounts), hostname) + ch <- t.containers.mustNewConstMetric(float64(data.Containers), hostname) + ch <- t.objects.mustNewConstMetric(float64(data.Objects), hostname) } + } else { + exitCode = 1 + logg.Error("swift recon: %s: %s", cmdArgsToStr(cmdArgs), err.Error()) + } + + ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), cmdArgsToStr(cmdArgs)) +} + +// reconUnmountedTask implements the collector.collectorTask interface. +type reconUnmountedTask struct { + pathToReconExecutable string + unmountedDrives typedDesc +} - ch <- prometheus.MustNewConstMetric( - clusterAccQuarantinedDesc, - prometheus.GaugeValue, float64(data.Accounts), - hostname, - ) - ch <- prometheus.MustNewConstMetric( - clusterCntrQuarantinedDesc, - prometheus.GaugeValue, float64(data.Containers), - hostname, - ) - ch <- prometheus.MustNewConstMetric( - clusterObjQuarantinedDesc, - prometheus.GaugeValue, float64(data.Objects), - hostname, - ) +func newReconUnmountedTask(pathToReconExecutable string) collectorTask { + return &reconUnmountedTask{ + pathToReconExecutable: pathToReconExecutable, + unmountedDrives: typedDesc{ + desc: prometheus.NewDesc( + "swift_cluster_drives_unmounted", + "Unmounted drives reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, } } -var clusterDrivesUnmountedDesc = prometheus.NewDesc( - "swift_cluster_drives_unmounted", - "Unmounted drives reported by the swift-recon tool.", - []string{"storage_ip"}, nil, -) +// reconUnmountedTask implements the collector.collectorTask interface. +func (t *reconUnmountedTask) describeMetrics(ch chan<- *prometheus.Desc) { + t.unmountedDrives.describe(ch) +} -func reconUnmountedTask(taskName, pathToReconExecutable string, ch chan<- prometheus.Metric) { - errCount := 0 +// reconUnmountedTask implements the collector.collectorTask interface. +func (t *reconUnmountedTask) collectMetrics(ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) { + exitCode := 0 cmdArgs := []string{"--unmounted", "--verbose"} - defer func() { - sendReconErrorCount(ch, cmdArgs, errCount) - }() + outputPerHost, err := getSwiftReconOutputPerHost(t.pathToReconExecutable, cmdArgs...) + if err == nil { + for hostname, dataBytes := range outputPerHost { + var disksData []struct { + Device string `json:"device"` + } + err := json.Unmarshal(dataBytes, &disksData) + if err != nil { + exitCode = 1 + logg.Error("swift dispersion: %s: %s: %s", cmdArgsToStr(cmdArgs), hostname, err.Error()) + continue // to next host + } - result, err := getSwiftReconOutputPerHost(pathToReconExecutable, cmdArgs...) - if err != nil { - logg.Error("recon collector: %s: %v", taskName, err) - errCount++ - return + ch <- t.unmountedDrives.mustNewConstMetric(float64(len(disksData)), hostname) + } + } else { + exitCode = 1 + logg.Error("swift recon: %s: %s", cmdArgsToStr(cmdArgs), err.Error()) } - for hostname, dataBytes := range result { - var disksData []struct { - Device string `json:"device"` - } - err := json.Unmarshal(dataBytes, &disksData) - if err != nil { - logg.Error("recon collector: %s: %s: %v", taskName, hostname, err) - errCount++ - continue - } + ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), cmdArgsToStr(cmdArgs)) +} + +// reconDriveAuditTask implements the collector.collectorTask interface. +type reconDriveAuditTask struct { + pathToReconExecutable string + auditErrors typedDesc +} - ch <- prometheus.MustNewConstMetric( - clusterDrivesUnmountedDesc, - prometheus.GaugeValue, float64(len(disksData)), - hostname, - ) +func newReconDriveAuditTask(pathToReconExecutable string) collectorTask { + return &reconDriveAuditTask{ + pathToReconExecutable: pathToReconExecutable, + auditErrors: typedDesc{ + desc: prometheus.NewDesc( + "swift_cluster_drives_audit_errors", + "Drive audit errors reported by the swift-recon tool.", + []string{"storage_ip"}, nil), + valueType: prometheus.GaugeValue, + }, } } -var clusterDrivesAuditErrsDesc = prometheus.NewDesc( - "swift_cluster_drives_audit_errors", - "Drive audit errors reported by the swift-recon tool.", - []string{"storage_ip"}, nil, -) +// reconDriveAuditTask implements the collector.collectorTask interface. +func (t *reconDriveAuditTask) describeMetrics(ch chan<- *prometheus.Desc) { + t.auditErrors.describe(ch) +} -func reconDriveAuditTask(taskName, pathToReconExecutable string, ch chan<- prometheus.Metric) { - errCount := 0 +// reconDriveAuditTask implements the collector.collectorTask interface. +func (t *reconDriveAuditTask) collectMetrics(ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) { + exitCode := 0 cmdArgs := []string{"--driveaudit", "--verbose"} - defer func() { - sendReconErrorCount(ch, cmdArgs, errCount) - }() - - result, err := getSwiftReconOutputPerHost(pathToReconExecutable, cmdArgs...) - if err != nil { - logg.Error("recon collector: %s: %v", taskName, err) - errCount++ - return - } + outputPerHost, err := getSwiftReconOutputPerHost(t.pathToReconExecutable, cmdArgs...) + if err == nil { + for hostname, dataBytes := range outputPerHost { + var data struct { + DriveAuditErrors int64 `json:"drive_audit_errors"` + } + err := json.Unmarshal(dataBytes, &data) + if err != nil { + exitCode = 1 + logg.Error("swift dispersion: %s: %s: %s", cmdArgsToStr(cmdArgs), hostname, err.Error()) + continue // to next host + } - for hostname, dataBytes := range result { - var data struct { - DriveAuditErrors int64 `json:"drive_audit_errors"` + ch <- t.auditErrors.mustNewConstMetric(float64(data.DriveAuditErrors), hostname) } - err := json.Unmarshal(dataBytes, &data) - if err != nil { - logg.Error("recon collector: %s: %s: %v", taskName, hostname, err) - errCount++ - continue - } - - ch <- prometheus.MustNewConstMetric( - clusterDrivesAuditErrsDesc, - prometheus.GaugeValue, float64(data.DriveAuditErrors), - hostname, - ) + } else { + exitCode = 1 + logg.Error("swift recon: %s: %s", cmdArgsToStr(cmdArgs), err.Error()) } + + ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), cmdArgsToStr(cmdArgs)) } /////////////////////////////////////////////////////////////////////////////// @@ -611,7 +658,7 @@ func getSwiftReconOutputPerHost(pathToExecutable string, cmdArgs ...string) (map matchList := reconHostOutputRx.FindAllSubmatch(out, -1) if len(matchList) == 0 { - return nil, fmt.Errorf("command %q did not return any usable output", cmd) + return nil, errors.New("command did not return any usable output") } result := make(map[string][]byte) From 027107064e4287306331ba3b45fecc67767e4923 Mon Sep 17 00:00:00 2001 From: Muhammad Talal Anwar Date: Tue, 14 Jan 2020 18:27:24 +0100 Subject: [PATCH 6/9] dispersion: add error test case --- collectors/dispersion_test.go | 18 +++++++- .../fixtures/dispersion_failed_collect.prom | 3 ++ ...rom => dispersion_successful_collect.prom} | 2 +- .../main.go | 41 +++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 collectors/fixtures/dispersion_failed_collect.prom rename collectors/fixtures/{dispersion_metrics.prom => dispersion_successful_collect.prom} (96%) create mode 100644 test/cmd/mock-swift-dispersion-report-with-errors/main.go diff --git a/collectors/dispersion_test.go b/collectors/dispersion_test.go index d0bac82..960c892 100644 --- a/collectors/dispersion_test.go +++ b/collectors/dispersion_test.go @@ -35,6 +35,22 @@ func TestDispersionCollector(t *testing.T) { Method: "GET", Path: "/metrics", ExpectStatus: 200, - ExpectBody: assert.FixtureFile("fixtures/dispersion_metrics.prom"), + ExpectBody: assert.FixtureFile("fixtures/dispersion_successful_collect.prom"), + }.Check(t, promhttp.HandlerFor(registry, promhttp.HandlerOpts{})) +} + +func TestDispersionCollectorWithErrors(t *testing.T) { + pathToExecutable, err := filepath.Abs("../build/mock-swift-dispersion-report-with-errors") + if err != nil { + t.Error(err) + } + + registry := prometheus.NewPedanticRegistry() + registry.MustRegister(NewDispersionCollector(pathToExecutable)) + assert.HTTPRequest{ + Method: "GET", + Path: "/metrics", + ExpectStatus: 200, + ExpectBody: assert.FixtureFile("fixtures/dispersion_failed_collect.prom"), }.Check(t, promhttp.HandlerFor(registry, promhttp.HandlerOpts{})) } diff --git a/collectors/fixtures/dispersion_failed_collect.prom b/collectors/fixtures/dispersion_failed_collect.prom new file mode 100644 index 0000000..89144b9 --- /dev/null +++ b/collectors/fixtures/dispersion_failed_collect.prom @@ -0,0 +1,3 @@ +# HELP swift_dispersion_task_exit_code The exit code for a Swift Dispersion Report query execution. +# TYPE swift_dispersion_task_exit_code gauge +swift_dispersion_task_exit_code{query="--dump-json"} 1 diff --git a/collectors/fixtures/dispersion_metrics.prom b/collectors/fixtures/dispersion_successful_collect.prom similarity index 96% rename from collectors/fixtures/dispersion_metrics.prom rename to collectors/fixtures/dispersion_successful_collect.prom index 8ba0a12..695bb7b 100644 --- a/collectors/fixtures/dispersion_metrics.prom +++ b/collectors/fixtures/dispersion_successful_collect.prom @@ -24,4 +24,4 @@ swift_dispersion_object_copies_missing 0 swift_dispersion_object_overlapping 0 # HELP swift_dispersion_task_exit_code The exit code for a Swift Dispersion Report query execution. # TYPE swift_dispersion_task_exit_code gauge -swift_dispersion_task_exit_code{failed_query="",task="reportDump"} 0 +swift_dispersion_task_exit_code{query="--dump-json"} 0 diff --git a/test/cmd/mock-swift-dispersion-report-with-errors/main.go b/test/cmd/mock-swift-dispersion-report-with-errors/main.go new file mode 100644 index 0000000..48fa0b2 --- /dev/null +++ b/test/cmd/mock-swift-dispersion-report-with-errors/main.go @@ -0,0 +1,41 @@ +// Copyright 2019 SAP SE +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "os" + + "github.com/alecthomas/kingpin" +) + +var reportData = []byte( + `ERROR: 10.0.0.1:6001/sdb-01: [Errno 111] ECONNREFUSED + ERROR: 10.0.0.1:6001/sdb-02: [Errno 111] ECONNREFUSED + ERROR: 10.0.0.1:6001/sdb-03: [Errno 111] ECONNREFUSED + ERROR: 10.0.0.1:6001/sdb-04: [Errno 111] ECONNREFUSED + ERROR: 10.0.0.1:6001/sdb-05: [Errno 111] ECONNREFUSED + ERROR: 10.0.0.2:6001/sdb-01: [Errno 111] ECONNREFUSED + ERROR: 10.0.0.2:6001/sdb-02: [Errno 111] ECONNREFUSED + ERROR: 10.0.0.2:6001/sdb-03: [Errno 111] ECONNREFUSED + ERROR: 10.0.0.2:6001/sdb-04: [Errno 111] ECONNREFUSED + ERROR: 10.0.0.2:6001/sdb-05: [Errno 111] ECONNREFUSED`) + +func main() { + dumpJSONFlag := kingpin.Flag("dump-json", "Dump dispersion report in json format.").Short('j').Required().Bool() + kingpin.Parse() + if *dumpJSONFlag { + os.Stdout.Write(reportData) + } +} From e79f055a5eca28f6789dca750cd8c0045da303e7 Mon Sep 17 00:00:00 2001 From: Muhammad Talal Anwar Date: Tue, 14 Jan 2020 18:29:21 +0100 Subject: [PATCH 7/9] recon: add error test case --- collectors/fixtures/recon_failed_collect.prom | 79 ++++++++ ...ics.prom => recon_successful_metrics.prom} | 17 +- collectors/recon_test.go | 18 +- test/cmd/mock-swift-recon-with-errors/main.go | 177 ++++++++++++++++++ 4 files changed, 283 insertions(+), 8 deletions(-) create mode 100644 collectors/fixtures/recon_failed_collect.prom rename collectors/fixtures/{recon_metrics.prom => recon_successful_metrics.prom} (93%) create mode 100644 test/cmd/mock-swift-recon-with-errors/main.go diff --git a/collectors/fixtures/recon_failed_collect.prom b/collectors/fixtures/recon_failed_collect.prom new file mode 100644 index 0000000..e7d088b --- /dev/null +++ b/collectors/fixtures/recon_failed_collect.prom @@ -0,0 +1,79 @@ +# HELP swift_cluster_accounts_quarantined Quarantined accounts reported by the swift-recon tool. +# TYPE swift_cluster_accounts_quarantined gauge +swift_cluster_accounts_quarantined{storage_ip="10.0.0.1"} 0 +# HELP swift_cluster_accounts_replication_age Account replication age reported by the swift-recon tool. +# TYPE swift_cluster_accounts_replication_age gauge +swift_cluster_accounts_replication_age{storage_ip="10.0.0.1"} 1.579007237099724e+09 +# HELP swift_cluster_accounts_replication_duration Account replication duration reported by the swift-recon tool. +# TYPE swift_cluster_accounts_replication_duration gauge +swift_cluster_accounts_replication_duration{storage_ip="10.0.0.1"} 23.422847032546997 +# HELP swift_cluster_containers_quarantined Quarantined containers reported by the swift-recon tool. +# TYPE swift_cluster_containers_quarantined gauge +swift_cluster_containers_quarantined{storage_ip="10.0.0.1"} 0 +# HELP swift_cluster_containers_replication_age Container replication age reported by the swift-recon tool. +# TYPE swift_cluster_containers_replication_age gauge +swift_cluster_containers_replication_age{storage_ip="10.0.0.1"} 1.579007236617117e+09 +# HELP swift_cluster_containers_replication_duration Container replication duration reported by the swift-recon tool. +# TYPE swift_cluster_containers_replication_duration gauge +swift_cluster_containers_replication_duration{storage_ip="10.0.0.1"} 98.37576985359192 +# HELP swift_cluster_containers_updater_sweep_time Container updater sweep time reported by the swift-recon tool. +# TYPE swift_cluster_containers_updater_sweep_time gauge +swift_cluster_containers_updater_sweep_time{storage_ip="10.0.0.1"} 54.06525897979736 +# HELP swift_cluster_drives_audit_errors Drive audit errors reported by the swift-recon tool. +# TYPE swift_cluster_drives_audit_errors gauge +swift_cluster_drives_audit_errors{storage_ip="10.0.0.1"} 0 +# HELP swift_cluster_drives_unmounted Unmounted drives reported by the swift-recon tool. +# TYPE swift_cluster_drives_unmounted gauge +swift_cluster_drives_unmounted{storage_ip="10.0.0.1"} 0 +# HELP swift_cluster_objects_quarantined Quarantined objects reported by the swift-recon tool. +# TYPE swift_cluster_objects_quarantined gauge +swift_cluster_objects_quarantined{storage_ip="10.0.0.1"} 0 +# HELP swift_cluster_objects_replication_age Object replication age reported by the swift-recon tool. +# TYPE swift_cluster_objects_replication_age gauge +swift_cluster_objects_replication_age{storage_ip="10.0.0.1"} 1.57900646181673e+09 +# HELP swift_cluster_objects_replication_duration Object replication duration reported by the swift-recon tool. +# TYPE swift_cluster_objects_replication_duration gauge +swift_cluster_objects_replication_duration{storage_ip="10.0.0.1"} 5.449508202075958 +# HELP swift_cluster_objects_updater_sweep_time Object updater sweep time reported by the swift-recon tool. +# TYPE swift_cluster_objects_updater_sweep_time gauge +swift_cluster_objects_updater_sweep_time{storage_ip="10.0.0.1"} 1.863548994064331 +# HELP swift_cluster_storage_capacity_bytes Capacity storage bytes as reported by the swift-recon tool. +# TYPE swift_cluster_storage_capacity_bytes gauge +swift_cluster_storage_capacity_bytes{storage_ip="10.0.0.1"} 8.3986504433664e+13 +# HELP swift_cluster_storage_free_bytes Free storage bytes as reported by the swift-recon tool. +# TYPE swift_cluster_storage_free_bytes gauge +swift_cluster_storage_free_bytes{storage_ip="10.0.0.1"} 7.8765751533568e+13 +# HELP swift_cluster_storage_used_bytes Used storage bytes as reported by the swift-recon tool. +# TYPE swift_cluster_storage_used_bytes gauge +swift_cluster_storage_used_bytes{storage_ip="10.0.0.1"} 5.220752900096e+12 +# HELP swift_cluster_storage_used_percent Fractional usage as reported by the swift-recon tool. +# TYPE swift_cluster_storage_used_percent gauge +swift_cluster_storage_used_percent{storage_ip="10.0.0.1"} 0.062161807248682026 +# HELP swift_cluster_storage_used_percent_by_disk Fractional usage of a disk as reported by the swift-recon tool. +# TYPE swift_cluster_storage_used_percent_by_disk gauge +swift_cluster_storage_used_percent_by_disk{disk="sdb01",storage_ip="10.0.0.1"} 0.06287556088217618 +swift_cluster_storage_used_percent_by_disk{disk="sdb02",storage_ip="10.0.0.1"} 0.06114153765139595 +swift_cluster_storage_used_percent_by_disk{disk="sdb03",storage_ip="10.0.0.1"} 0.060846766709053014 +swift_cluster_storage_used_percent_by_disk{disk="sdb04",storage_ip="10.0.0.1"} 0.06247550445917624 +swift_cluster_storage_used_percent_by_disk{disk="sdb05",storage_ip="10.0.0.1"} 0.0636920166631899 +swift_cluster_storage_used_percent_by_disk{disk="sdb06",storage_ip="10.0.0.1"} 0.06184825898097433 +swift_cluster_storage_used_percent_by_disk{disk="sdb07",storage_ip="10.0.0.1"} 0.06449695878640205 +swift_cluster_storage_used_percent_by_disk{disk="sdb08",storage_ip="10.0.0.1"} 0.06037474702299367 +swift_cluster_storage_used_percent_by_disk{disk="sdb09",storage_ip="10.0.0.1"} 0.06453964869569374 +swift_cluster_storage_used_percent_by_disk{disk="sdb10",storage_ip="10.0.0.1"} 0.061613682968306 +swift_cluster_storage_used_percent_by_disk{disk="sdb11",storage_ip="10.0.0.1"} 0.061911459492196855 +swift_cluster_storage_used_percent_by_disk{disk="sdb12",storage_ip="10.0.0.1"} 0.06452540666354745 +swift_cluster_storage_used_percent_by_disk{disk="sdb13",storage_ip="10.0.0.1"} 0.061085069997884475 +swift_cluster_storage_used_percent_by_disk{disk="sdb14",storage_ip="10.0.0.1"} 0.05883868250855854 +# HELP swift_recon_task_exit_code The exit code for a Swift Recon query execution. +# TYPE swift_recon_task_exit_code gauge +swift_recon_task_exit_code{query="--diskusage --verbose"} 1 +swift_recon_task_exit_code{query="--driveaudit --verbose"} 1 +swift_recon_task_exit_code{query="--md5"} 1 +swift_recon_task_exit_code{query="--quarantined --verbose"} 1 +swift_recon_task_exit_code{query="--unmounted --verbose"} 1 +swift_recon_task_exit_code{query="account --replication --verbose"} 1 +swift_recon_task_exit_code{query="container --replication --verbose"} 1 +swift_recon_task_exit_code{query="container --updater --verbose"} 1 +swift_recon_task_exit_code{query="object --replication --verbose"} 1 +swift_recon_task_exit_code{query="object --updater --verbose"} 1 diff --git a/collectors/fixtures/recon_metrics.prom b/collectors/fixtures/recon_successful_metrics.prom similarity index 93% rename from collectors/fixtures/recon_metrics.prom rename to collectors/fixtures/recon_successful_metrics.prom index 44bb896..03708dc 100644 --- a/collectors/fixtures/recon_metrics.prom +++ b/collectors/fixtures/recon_successful_metrics.prom @@ -114,10 +114,13 @@ swift_cluster_storage_used_percent_by_disk{disk="sdb14",storage_ip="10.0.0.1"} 0 swift_cluster_storage_used_percent_by_disk{disk="sdb14",storage_ip="10.0.0.2"} 0.06518201289089147 # HELP swift_recon_task_exit_code The exit code for a Swift Recon query execution. # TYPE swift_recon_task_exit_code gauge -swift_recon_task_exit_code{failed_query="",task="diskUsage"} 0 -swift_recon_task_exit_code{failed_query="",task="driveAudit"} 0 -swift_recon_task_exit_code{failed_query="",task="md5"} 0 -swift_recon_task_exit_code{failed_query="",task="quarantined"} 0 -swift_recon_task_exit_code{failed_query="",task="replication"} 0 -swift_recon_task_exit_code{failed_query="",task="unmounted"} 0 -swift_recon_task_exit_code{failed_query="",task="updaterSweep"} 0 +swift_recon_task_exit_code{query="--diskusage --verbose"} 0 +swift_recon_task_exit_code{query="--driveaudit --verbose"} 0 +swift_recon_task_exit_code{query="--md5"} 0 +swift_recon_task_exit_code{query="--quarantined --verbose"} 0 +swift_recon_task_exit_code{query="--unmounted --verbose"} 0 +swift_recon_task_exit_code{query="account --replication --verbose"} 0 +swift_recon_task_exit_code{query="container --replication --verbose"} 0 +swift_recon_task_exit_code{query="container --updater --verbose"} 0 +swift_recon_task_exit_code{query="object --replication --verbose"} 0 +swift_recon_task_exit_code{query="object --updater --verbose"} 0 diff --git a/collectors/recon_test.go b/collectors/recon_test.go index ace1b06..cfbfdde 100644 --- a/collectors/recon_test.go +++ b/collectors/recon_test.go @@ -35,6 +35,22 @@ func TestReconCollector(t *testing.T) { Method: "GET", Path: "/metrics", ExpectStatus: 200, - ExpectBody: assert.FixtureFile("fixtures/recon_metrics.prom"), + ExpectBody: assert.FixtureFile("fixtures/recon_successful_metrics.prom"), + }.Check(t, promhttp.HandlerFor(registry, promhttp.HandlerOpts{})) +} + +func TestReconCollectorWithErrors(t *testing.T) { + pathToExecutable, err := filepath.Abs("../build/mock-swift-recon-with-errors") + if err != nil { + t.Error(err) + } + + registry := prometheus.NewPedanticRegistry() + registry.MustRegister(NewReconCollector(pathToExecutable)) + assert.HTTPRequest{ + Method: "GET", + Path: "/metrics", + ExpectStatus: 200, + ExpectBody: assert.FixtureFile("fixtures/recon_failed_collect.prom"), }.Check(t, promhttp.HandlerFor(registry, promhttp.HandlerOpts{})) } diff --git a/test/cmd/mock-swift-recon-with-errors/main.go b/test/cmd/mock-swift-recon-with-errors/main.go new file mode 100644 index 0000000..83767d4 --- /dev/null +++ b/test/cmd/mock-swift-recon-with-errors/main.go @@ -0,0 +1,177 @@ +// Copyright 2019 SAP SE +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "os" + + "github.com/alecthomas/kingpin" +) + +func main() { + serverTypeArg := kingpin.Arg("server-type", "Type of server.").Enum("account", "container", "object") + verboseFlag := kingpin.Flag("verbose", "Print verbose info.").Short('v').Bool() + diskUsageFlag := kingpin.Flag("diskusage", "Get disk usage stats.").Short('d').Bool() + driveAuditFlag := kingpin.Flag("driveaudit", "Get drive audit error stats.").Bool() + md5Flag := kingpin.Flag("md5", "Get md5sum of servers ring and compare to local copy.").Bool() + quarantinedFlag := kingpin.Flag("quarantined", "Get cluster quarantine stats.").Short('q').Bool() + replicationFlag := kingpin.Flag("replication", "Get replication stats.").Short('r').Bool() + unmountedFlag := kingpin.Flag("unmounted", "Check cluster for unmounted devices.").Short('u').Bool() + updaterFlag := kingpin.Flag("updater", "Get updater stats.").Bool() + + kingpin.Parse() + switch { + case *diskUsageFlag && *verboseFlag: + os.Stdout.Write(diskUsageVerboseData) + case *driveAuditFlag && *verboseFlag: + os.Stdout.Write(driveAuditVerboseData) + case *md5Flag: + os.Stdout.Write(md5Data) + case *quarantinedFlag && *verboseFlag: + os.Stdout.Write(quarantinedVerboseData) + case *replicationFlag && *verboseFlag: + switch *serverTypeArg { + case "account": + os.Stdout.Write(accountReplVerboseData) + case "container": + os.Stdout.Write(containerReplVerboseData) + case "object": + os.Stdout.Write(objectReplVerboseData) + } + case *unmountedFlag && *verboseFlag: + os.Stdout.Write(unmountedVerboseData) + case *updaterFlag && *verboseFlag: + switch *serverTypeArg { + case "container": + os.Stdout.Write(containerUpdaterVerboseData) + case "object": + os.Stdout.Write(objectUpdaterVerboseData) + } + } +} + +var diskUsageVerboseData = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (object) +=============================================================================== +[2020-01-14 12:55:00] Checking disk usage now +-> http://10.0.0.1:6000/recon/diskusage: [{u'device': u'sdb-14', u'avail': 5646060654592, u'mounted': True, u'used': 352975376384, u'size': 5999036030976}, {u'device': u'sdb-03', u'avail': 5634014085120, u'mounted': True, u'used': 365021945856, u'size': 5999036030976}, {u'device': u'sdb-09', u'avail': 5611860353024, u'mounted': True, u'used': 387175677952, u'size': 5999036030976}, {u'device': u'sdb-10', u'avail': 5629413326848, u'mounted': True, u'used': 369622704128, u'size': 5999036030976}, {u'device': u'sdb-06', u'avail': 5628006096896, u'mounted': True, u'used': 371029934080, u'size': 5999036030976}, {u'device': u'sdb-07', u'avail': 5612116451328, u'mounted': True, u'used': 386919579648, u'size': 5999036030976}, {u'device': u'sdb-04', u'avail': 5624243228672, u'mounted': True, u'used': 374792802304, u'size': 5999036030976}, {u'device': u'sdb-05', u'avail': 5616945328128, u'mounted': True, u'used': 382090702848, u'size': 5999036030976}, {u'device': u'sdb-01', u'avail': 5621843275776, u'mounted': True, u'used': 377192755200, u'size': 5999036030976}, {u'device': u'sdb-08', u'avail': 5636845748224, u'mounted': True, u'used': 362190282752, u'size': 5999036030976}, {u'device': u'sdb-12', u'avail': 5611945791488, u'mounted': True, u'used': 387090239488, u'size': 5999036030976}, {u'device': u'sdb-11', u'avail': 5627626954752, u'mounted': True, u'used': 371409076224, u'size': 5999036030976}, {u'device': u'sdb-13', u'avail': 5632584495104, u'mounted': True, u'used': 366451535872, u'size': 5999036030976}, {u'device': u'sdb-02', u'avail': 5632245743616, u'mounted': True, u'used': 366790287360, u'size': 5999036030976}] +-> http://10.0.0.2:6000/recon/diskusage: +Distribution Graph: + 5% 1 ***** + 6% 13 ********************************************************************* +Disk usage: space used: 5220752900096 of 83986504433664 +Disk usage: space free: 78765751533568 of 83986504433664 +Disk usage: lowest: 5.88%, highest: 6.45%, avg: 6.21618072487% +===============================================================================`) + +var md5Data = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (object) +=============================================================================== +[2020-01-14 12:55:30] Checking ring md5sums +-> http://10.0.0.2:6000/recon/ringmd5: +1/2 hosts matched, 1 error[s] while checking hosts. +=============================================================================== +[2020-01-14 12:55:35] Checking swift.conf md5sum +-> http://10.0.0.2:6000/recon/swiftconfmd5: +1/2 hosts matched, 1 error[s] while checking hosts. +===============================================================================`) + +var containerUpdaterVerboseData = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (container) +=============================================================================== +[2020-01-14 13:08:17] Checking updater times +-> http://10.0.0.1:6001/recon/updater/container: {u'container_updater_sweep': 54.06525897979736} +-> http://10.0.0.2:6001/recon/updater/container: +[updater_last_sweep] low: 54, high: 54, avg: 54.1, total: 54, Failed: 0.0%, no_result: 0, reported: 1 +===============================================================================`) + +var objectUpdaterVerboseData = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (object) +=============================================================================== +[2020-01-14 13:08:07] Checking updater times +-> http://10.0.0.1:6000/recon/updater/object: {u'object_updater_sweep': 1.863548994064331} +-> http://10.0.0.2:6000/recon/updater/object: +[updater_last_sweep] low: 1, high: 1, avg: 1.9, total: 1, Failed: 0.0%, no_result: 0, reported: 1 +===============================================================================`) + +var accountReplVerboseData = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (account) +=============================================================================== +[2020-01-14 13:07:32] Checking on replication +-> http://10.0.0.1:6002/recon/replication/account: {u'replication_last': 1579007237.099724, u'replication_stats': {u'no_change': 408, u'rsync': 0, u'success': 410, u'failure': 816, u'attempted': 613, u'ts_repl': 0, u'remove': 0, u'remote_merge': 0, u'diff_capped': 0, u'deferred': 0, u'hashmatch': 0, u'failure_nodes': {u'10.0.0.2': {u'sdb-11': 73, u'sdb-10': 57, u'sdb-13': 53, u'sdb-12': 49, u'sdb-14': 48, u'sdb-08': 54, u'sdb-09': 56, u'sdb-06': 64, u'sdb-07': 60, u'sdb-04': 67, u'sdb-05': 58, u'sdb-02': 63, u'sdb-03': 59, u'sdb-01': 55}}, u'diff': 2, u'start': 1579007213.676877, u'empty': 0}, u'replication_time': 23.422847032546997} +-> http://10.0.0.2:6002/recon/replication/account: +[replication_failure] low: 816, high: 816, avg: 816.0, total: 816, Failed: 0.0%, no_result: 0, reported: 1 +[replication_success] low: 410, high: 410, avg: 410.0, total: 410, Failed: 0.0%, no_result: 0, reported: 1 +[replication_time] low: 23, high: 23, avg: 23.4, total: 23, Failed: 0.0%, no_result: 0, reported: 1 +[replication_attempted] low: 613, high: 613, avg: 613.0, total: 613, Failed: 0.0%, no_result: 0, reported: 1 +Oldest completion was 2020-01-14 13:07:17 (20 seconds ago) by 10.0.0.1:6002. +Most recent completion was 2020-01-14 13:07:17 (20 seconds ago) by 10.0.0.1:6002. +===============================================================================`) + +var containerReplVerboseData = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (container) +=============================================================================== +[2020-01-14 13:07:42] Checking on replication +-> http://10.0.0.1:6001/recon/replication/container: {u'replication_last': 1579007236.617117, u'replication_stats': {u'no_change': 7963, u'rsync': 0, u'success': 7966, u'failure': 814, u'attempted': 4390, u'ts_repl': 0, u'remove': 0, u'remote_merge': 0, u'diff_capped': 0, u'deferred': 0, u'hashmatch': 0, u'failure_nodes': {u'10.0.0.2': {u'sdb-11': 42, u'sdb-10': 76, u'sdb-13': 69, u'sdb-12': 47, u'sdb-14': 73, u'sdb-08': 63, u'sdb-09': 41, u'sdb-06': 51, u'sdb-07': 55, u'sdb-04': 61, u'sdb-05': 66, u'sdb-02': 61, u'sdb-03': 71, u'sdb-01': 38}}, u'diff': 3, u'start': 1579007138.241347, u'empty': 0}, u'replication_time': 98.37576985359192} +-> http://10.0.0.2:6001/recon/replication/container: +[replication_failure] low: 814, high: 814, avg: 814.0, total: 814, Failed: 0.0%, no_result: 0, reported: 1 +[replication_success] low: 7966, high: 7966, avg: 7966.0, total: 7966, Failed: 0.0%, no_result: 0, reported: 1 +[replication_time] low: 98, high: 98, avg: 98.4, total: 98, Failed: 0.0%, no_result: 0, reported: 1 +[replication_attempted] low: 4390, high: 4390, avg: 4390.0, total: 4390, Failed: 0.0%, no_result: 0, reported: 1 +Oldest completion was 2020-01-14 13:07:16 (30 seconds ago) by 10.0.0.1:6001. +Most recent completion was 2020-01-14 13:07:16 (30 seconds ago) by 10.0.0.1:6001. +===============================================================================`) + +var objectReplVerboseData = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (object) +=============================================================================== +[2020-01-14 13:07:56] Checking on replication +-> http://10.0.0.1:6000/recon/replication/object: {u'replication_last': 1579006461.81673, u'replication_stats': {u'rsync': 9, u'success': 168394, u'failure': 28214, u'attempted': 98304, u'remove': 0, u'suffix_count': 1267858, u'start': 1535617859.516976, u'hashmatch': 168393, u'failure_nodes': {u'10.0.0.1': {u'sdb-12': 3, u'sdb-02': 1}, u'10.0.0.2': {u'sdb-11': 2013, u'sdb-10': 2021, u'sdb-13': 2037, u'sdb-12': 2061, u'sdb-14': 1976, u'sdb-08': 1915, u'sdb-09': 2018, u'sdb-06': 2016, u'sdb-07': 2008, u'sdb-04': 2112, u'sdb-05': 2007, u'sdb-02': 1988, u'sdb-03': 2041, u'sdb-01': 1997}}, u'suffix_sync': 1, u'suffix_hash': 7}, u'replication_time': 5.449508202075958, u'object_replication_last': 1579006461.81673, u'object_replication_time': 5.449508202075958} +-> http://10.0.0.2:6000/recon/replication/object: +[replication_failure] low: 28214, high: 28214, avg: 28214.0, total: 28214, Failed: 0.0%, no_result: 0, reported: 1 +[replication_success] low: 168394, high: 168394, avg: 168394.0, total: 168394, Failed: 0.0%, no_result: 0, reported: 1 +[replication_time] low: 5, high: 5, avg: 5.4, total: 5, Failed: 0.0%, no_result: 0, reported: 1 +[replication_attempted] low: 98304, high: 98304, avg: 98304.0, total: 98304, Failed: 0.0%, no_result: 0, reported: 1 +Oldest completion was 2020-01-14 12:54:21 (13 minutes ago) by 10.0.0.1:6000. +Most recent completion was 2020-01-14 12:54:21 (13 minutes ago) by 10.0.0.1:6000. +===============================================================================`) + +var quarantinedVerboseData = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (object) +=============================================================================== +[2020-01-14 12:56:01] Checking quarantine +-> http://10.0.0.1:6000/recon/quarantined: {u'objects': 0, u'accounts': 0, u'containers': 0, u'policies': {}} +-> http://10.0.0.2:6000/recon/quarantined: +[quarantined_objects] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 1 +[quarantined_accounts] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 1 +[quarantined_containers] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 1 +===============================================================================`) + +var unmountedVerboseData = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (object) +=============================================================================== +[2020-01-14 12:57:02] Getting unmounted drives from 2 hosts... +-> http://10.0.0.1:6000/recon/unmounted: [] +-> http://10.0.0.2:6000/recon/unmounted: +===============================================================================`) + +var driveAuditVerboseData = []byte(`=============================================================================== +--> Starting reconnaissance on 2 hosts (object) +=============================================================================== +[2020-01-14 12:55:44] Checking drive-audit errors +-> http://10.0.0.1:6000/recon/driveaudit: {u'drive_audit_errors': 0} +-> http://10.0.0.2:6000/recon/driveaudit: +[drive_audit_errors] low: 0, high: 0, avg: 0.0, total: 0, Failed: 0.0%, no_result: 0, reported: 1 +===============================================================================`) From 3be191353fe154ff94b8bfeecd30c5bb2f7724e5 Mon Sep 17 00:00:00 2001 From: Muhammad Talal Anwar Date: Tue, 14 Jan 2020 18:29:51 +0100 Subject: [PATCH 8/9] Add quick-check rule to Makefile --- Makefile | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 326beda..6b7e866 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ static-check: FORCE @$(GO) vet $(GO_BUILDFLAGS) $(GO_ALLPKGS) # detailed unit test run (incl. test coverage) -build/%.cover.out: FORCE build/mock-swift-dispersion-report build/mock-swift-recon +build/%.cover.out: FORCE build/mock-tools @printf "\e[1;36m>> go test $(subst _,/,$*)\e[0m\n" $(GO) test $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' -coverprofile=$@ -covermode=count -coverpkg=$(subst $(space),$(comma),$(GO_COVERPKGS)) $(subst _,/,$*) build/cover.out: $(GO_COVERFILES) @@ -51,11 +51,18 @@ build/cover.out: $(GO_COVERFILES) build/cover.html: build/cover.out $(GO) tool cover -html $< -o $@ -build/mock-swift-dispersion-report: FORCE - $(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-dispersion-report' +# quick unit test run +quick-check: FORCE all build/mock-tools $(addprefix quick-check-,$(subst /,_,$(GO_TESTPKGS))) +quick-check-%: + @printf "\e[1;36m>> go test $(subst _,/,$*)\e[0m\n" + $(GO) test $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' $(subst _,/,$*) + @printf "\e[1;32m>> Unit tests successful.\e[0m\n" -build/mock-swift-recon: FORCE +build/mock-tools: FORCE + $(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-dispersion-report' + $(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-dispersion-report-with-errors' $(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-recon' + $(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-recon-with-errors' clean: FORCE rm -rf -- build From 10e1e597df73773acb38b99c1480ce9eeccc1110 Mon Sep 17 00:00:00 2001 From: Muhammad Talal Anwar Date: Tue, 14 Jan 2020 18:34:44 +0100 Subject: [PATCH 9/9] Rename package collectors -> collector --- Makefile | 2 +- {collectors => collector}/collector.go | 2 +- {collectors => collector}/dispersion.go | 2 +- {collectors => collector}/dispersion_test.go | 2 +- .../fixtures/dispersion_failed_collect.prom | 0 .../fixtures/dispersion_successful_collect.prom | 0 .../fixtures/recon_failed_collect.prom | 0 .../fixtures/recon_successful_metrics.prom | 0 {collectors => collector}/recon.go | 2 +- {collectors => collector}/recon_test.go | 2 +- main.go | 6 +++--- 11 files changed, 9 insertions(+), 9 deletions(-) rename {collectors => collector}/collector.go (98%) rename {collectors => collector}/dispersion.go (99%) rename {collectors => collector}/dispersion_test.go (98%) rename {collectors => collector}/fixtures/dispersion_failed_collect.prom (100%) rename {collectors => collector}/fixtures/dispersion_successful_collect.prom (100%) rename {collectors => collector}/fixtures/recon_failed_collect.prom (100%) rename {collectors => collector}/fixtures/recon_successful_metrics.prom (100%) rename {collectors => collector}/recon.go (99%) rename {collectors => collector}/recon_test.go (98%) diff --git a/Makefile b/Makefile index 6b7e866..e81278e 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ GO_ALLPKGS := $(shell go list $(GO_BUILDFLAGS) $(PKG)/...) # which packages to test with `go test`? GO_TESTPKGS := $(shell go list $(GO_BUILDFLAGS) -f '{{if .TestGoFiles}}{{.ImportPath}}{{end}}' $(PKG)/...) # which packages to measure coverage for? -GO_COVERPKGS := $(shell go list $(GO_BUILDFLAGS) $(PKG) $(PKG)/collectors) +GO_COVERPKGS := $(shell go list $(GO_BUILDFLAGS) $(PKG) $(PKG)/collector) # output files from `go test` GO_COVERFILES := $(patsubst %,build/%.cover.out,$(subst /,_,$(GO_TESTPKGS))) diff --git a/collectors/collector.go b/collector/collector.go similarity index 98% rename from collectors/collector.go rename to collector/collector.go index 5b85015..cd22620 100644 --- a/collectors/collector.go +++ b/collector/collector.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package collectors +package collector import ( "strings" diff --git a/collectors/dispersion.go b/collector/dispersion.go similarity index 99% rename from collectors/dispersion.go rename to collector/dispersion.go index af2805d..efd03f4 100644 --- a/collectors/dispersion.go +++ b/collector/dispersion.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package collectors +package collector import ( "encoding/json" diff --git a/collectors/dispersion_test.go b/collector/dispersion_test.go similarity index 98% rename from collectors/dispersion_test.go rename to collector/dispersion_test.go index 960c892..efb55bf 100644 --- a/collectors/dispersion_test.go +++ b/collector/dispersion_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package collectors +package collector import ( "path/filepath" diff --git a/collectors/fixtures/dispersion_failed_collect.prom b/collector/fixtures/dispersion_failed_collect.prom similarity index 100% rename from collectors/fixtures/dispersion_failed_collect.prom rename to collector/fixtures/dispersion_failed_collect.prom diff --git a/collectors/fixtures/dispersion_successful_collect.prom b/collector/fixtures/dispersion_successful_collect.prom similarity index 100% rename from collectors/fixtures/dispersion_successful_collect.prom rename to collector/fixtures/dispersion_successful_collect.prom diff --git a/collectors/fixtures/recon_failed_collect.prom b/collector/fixtures/recon_failed_collect.prom similarity index 100% rename from collectors/fixtures/recon_failed_collect.prom rename to collector/fixtures/recon_failed_collect.prom diff --git a/collectors/fixtures/recon_successful_metrics.prom b/collector/fixtures/recon_successful_metrics.prom similarity index 100% rename from collectors/fixtures/recon_successful_metrics.prom rename to collector/fixtures/recon_successful_metrics.prom diff --git a/collectors/recon.go b/collector/recon.go similarity index 99% rename from collectors/recon.go rename to collector/recon.go index 8e54916..c3336bb 100644 --- a/collectors/recon.go +++ b/collector/recon.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package collectors +package collector import ( "bytes" diff --git a/collectors/recon_test.go b/collector/recon_test.go similarity index 98% rename from collectors/recon_test.go rename to collector/recon_test.go index cfbfdde..e99e8f4 100644 --- a/collectors/recon_test.go +++ b/collector/recon_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package collectors +package collector import ( "path/filepath" diff --git a/main.go b/main.go index 76c661a..b01649e 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sapcc/go-bits/httpee" "github.com/sapcc/go-bits/logg" - "github.com/sapcc/swift-health-exporter/collectors" + "github.com/sapcc/swift-health-exporter/collector" ) func main() { @@ -33,8 +33,8 @@ func main() { swiftDispersionReportPath := getExecutablePath("SWIFT_DISPERSION_REPORT_PATH", "swift-dispersion-report") swiftReconPath := getExecutablePath("SWIFT_RECON_PATH", "swift-recon") - prometheus.MustRegister(collectors.NewDispersionCollector(swiftDispersionReportPath)) - prometheus.MustRegister(collectors.NewReconCollector(swiftReconPath)) + prometheus.MustRegister(collector.NewDispersionCollector(swiftDispersionReportPath)) + prometheus.MustRegister(collector.NewReconCollector(swiftReconPath)) // this port has been allocated for Swift health exporter // See: https://github.com/prometheus/prometheus/wiki/Default-port-allocations