1
0
mirror of https://github.com/prometheus-community/postgres_exporter.git synced 2025-08-08 04:42:07 +03:00

Update vendored tools/.

This commit is contained in:
Will Rouesnel
2017-08-03 21:19:17 +10:00
parent 1afbd62ab1
commit a7ff84a674
22 changed files with 1452 additions and 1109 deletions

View File

@@ -0,0 +1,384 @@
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/google/shlex"
"gopkg.in/alecthomas/kingpin.v3-unstable"
)
type Vars map[string]string
func (v Vars) Copy() Vars {
out := Vars{}
for k, v := range v {
out[k] = v
}
return out
}
func (v Vars) Replace(s string) string {
for k, v := range v {
prefix := regexp.MustCompile(fmt.Sprintf("{%s=([^}]*)}", k))
if v != "" {
s = prefix.ReplaceAllString(s, "$1")
} else {
s = prefix.ReplaceAllString(s, "")
}
s = strings.Replace(s, fmt.Sprintf("{%s}", k), v, -1)
}
return s
}
// Severity of linter message.
type Severity string
// Linter message severity levels.
const ( // nolint: deadcode
Error Severity = "error"
Warning Severity = "warning"
)
type Issue struct {
Linter string `json:"linter"`
Severity Severity `json:"severity"`
Path string `json:"path"`
Line int `json:"line"`
Col int `json:"col"`
Message string `json:"message"`
}
func (i *Issue) String() string {
buf := new(bytes.Buffer)
err := formatTemplate.Execute(buf, i)
kingpin.FatalIfError(err, "Invalid output format")
return buf.String()
}
type linterState struct {
*Linter
paths []string
issues chan *Issue
vars Vars
exclude *regexp.Regexp
include *regexp.Regexp
deadline <-chan time.Time
}
func (l *linterState) Partitions() ([][]string, error) {
command := l.vars.Replace(l.Command)
cmdArgs, err := parseCommand(command)
if err != nil {
return nil, err
}
parts, err := l.Linter.PartitionStrategy(cmdArgs, l.paths)
if err != nil {
return nil, err
}
return parts, nil
}
func runLinters(linters map[string]*Linter, paths []string, concurrency int, exclude, include *regexp.Regexp) (chan *Issue, chan error) {
errch := make(chan error, len(linters))
concurrencych := make(chan bool, concurrency)
incomingIssues := make(chan *Issue, 1000000)
processedIssues := filterIssuesViaDirectives(
newDirectiveParser(),
maybeSortIssues(maybeAggregateIssues(incomingIssues)))
vars := Vars{
"duplthreshold": fmt.Sprintf("%d", config.DuplThreshold),
"mincyclo": fmt.Sprintf("%d", config.Cyclo),
"maxlinelength": fmt.Sprintf("%d", config.LineLength),
"min_confidence": fmt.Sprintf("%f", config.MinConfidence),
"min_occurrences": fmt.Sprintf("%d", config.MinOccurrences),
"min_const_length": fmt.Sprintf("%d", config.MinConstLength),
"tests": "",
}
if config.Test {
vars["tests"] = "-t"
}
wg := &sync.WaitGroup{}
for _, linter := range linters {
deadline := time.After(config.Deadline.Duration())
state := &linterState{
Linter: linter,
issues: incomingIssues,
paths: paths,
vars: vars,
exclude: exclude,
include: include,
deadline: deadline,
}
partitions, err := state.Partitions()
if err != nil {
errch <- err
continue
}
for _, args := range partitions {
wg.Add(1)
// Call the goroutine with a copy of the args array so that the
// contents of the array are not modified by the next iteration of
// the above for loop
go func(args []string) {
concurrencych <- true
err := executeLinter(state, args)
if err != nil {
errch <- err
}
<-concurrencych
wg.Done()
}(append(args))
}
}
go func() {
wg.Wait()
close(incomingIssues)
close(errch)
}()
return processedIssues, errch
}
func executeLinter(state *linterState, args []string) error {
if len(args) == 0 {
return fmt.Errorf("missing linter command")
}
start := time.Now()
debug("executing %s", strings.Join(args, " "))
buf := bytes.NewBuffer(nil)
command := args[0]
cmd := exec.Command(command, args[1:]...) // nolint: gas
cmd.Stdout = buf
cmd.Stderr = buf
err := cmd.Start()
if err != nil {
return fmt.Errorf("failed to execute linter %s: %s", command, err)
}
done := make(chan bool)
go func() {
err = cmd.Wait()
done <- true
}()
// Wait for process to complete or deadline to expire.
select {
case <-done:
case <-state.deadline:
err = fmt.Errorf("deadline exceeded by linter %s (try increasing --deadline)",
state.Name)
kerr := cmd.Process.Kill()
if kerr != nil {
warning("failed to kill %s: %s", state.Name, kerr)
}
return err
}
if err != nil {
debug("warning: %s returned %s: %s", command, err, buf.String())
}
processOutput(state, buf.Bytes())
elapsed := time.Since(start)
debug("%s linter took %s", state.Name, elapsed)
return nil
}
func parseCommand(command string) ([]string, error) {
args, err := shlex.Split(command)
if err != nil {
return nil, err
}
if len(args) == 0 {
return nil, fmt.Errorf("invalid command %q", command)
}
exe, err := exec.LookPath(args[0])
if err != nil {
return nil, err
}
return append([]string{exe}, args[1:]...), nil
}
// nolint: gocyclo
func processOutput(state *linterState, out []byte) {
re := state.regex
all := re.FindAllSubmatchIndex(out, -1)
debug("%s hits %d: %s", state.Name, len(all), state.Pattern)
cwd, err := os.Getwd()
if err != nil {
warning("failed to get working directory %s", err)
}
// Create a local copy of vars so they can be modified by the linter output
vars := state.vars.Copy()
for _, indices := range all {
group := [][]byte{}
for i := 0; i < len(indices); i += 2 {
var fragment []byte
if indices[i] != -1 {
fragment = out[indices[i]:indices[i+1]]
}
group = append(group, fragment)
}
issue := &Issue{Line: 1, Linter: state.Linter.Name}
for i, name := range re.SubexpNames() {
if group[i] == nil {
continue
}
part := string(group[i])
if name != "" {
vars[name] = part
}
switch name {
case "path":
issue.Path = relativePath(cwd, part)
case "line":
n, err := strconv.ParseInt(part, 10, 32)
kingpin.FatalIfError(err, "line matched invalid integer")
issue.Line = int(n)
case "col":
n, err := strconv.ParseInt(part, 10, 32)
kingpin.FatalIfError(err, "col matched invalid integer")
issue.Col = int(n)
case "message":
issue.Message = part
case "":
}
}
// TODO: set messageOveride and severity on the Linter instead of reading
// them directly from the static config
if m, ok := config.MessageOverride[state.Name]; ok {
issue.Message = vars.Replace(m)
}
if sev, ok := config.Severity[state.Name]; ok {
issue.Severity = Severity(sev)
} else {
issue.Severity = Warning
}
if state.exclude != nil && state.exclude.MatchString(issue.String()) {
continue
}
if state.include != nil && !state.include.MatchString(issue.String()) {
continue
}
state.issues <- issue
}
}
func relativePath(root, path string) string {
fallback := path
root = resolvePath(root)
path = resolvePath(path)
var err error
path, err = filepath.Rel(root, path)
if err != nil {
warning("failed to make %s a relative path: %s", fallback, err)
return fallback
}
return path
}
func resolvePath(path string) string {
var err error
fallback := path
if !filepath.IsAbs(path) {
path, err = filepath.Abs(path)
if err != nil {
warning("failed to make %s an absolute path: %s", fallback, err)
return fallback
}
}
path, err = filepath.EvalSymlinks(path)
if err != nil {
warning("failed to resolve symlinks in %s: %s", fallback, err)
return fallback
}
return path
}
type sortedIssues struct {
issues []*Issue
order []string
}
func (s *sortedIssues) Len() int { return len(s.issues) }
func (s *sortedIssues) Swap(i, j int) { s.issues[i], s.issues[j] = s.issues[j], s.issues[i] }
// nolint: gocyclo
func (s *sortedIssues) Less(i, j int) bool {
l, r := s.issues[i], s.issues[j]
for _, key := range s.order {
switch key {
case "path":
if l.Path > r.Path {
return false
}
case "line":
if l.Line > r.Line {
return false
}
case "column":
if l.Col > r.Col {
return false
}
case "severity":
if l.Severity > r.Severity {
return false
}
case "message":
if l.Message > r.Message {
return false
}
case "linter":
if l.Linter > r.Linter {
return false
}
}
}
return true
}
func maybeSortIssues(issues chan *Issue) chan *Issue {
if reflect.DeepEqual([]string{"none"}, config.Sort) {
return issues
}
out := make(chan *Issue, 1000000)
sorted := &sortedIssues{
issues: []*Issue{},
order: config.Sort,
}
go func() {
for issue := range issues {
sorted.issues = append(sorted.issues, issue)
}
sort.Sort(sorted)
for _, issue := range sorted.issues {
out <- issue
}
close(out)
}()
return out
}