-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added running haproxy binary in check mode, release 0.3.0
- Loading branch information
Showing
7 changed files
with
287 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package lib | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"io/ioutil" | ||
"os/exec" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// ParseHaproxyLine parses line of HAProxy config check output | ||
func ParseHaproxyLine(line string) *Problem { | ||
regexps := []*regexp.Regexp{ | ||
regexp.MustCompile(`\[(?P<severity>\w+)\]\s+\d+\/\d+\s+\(\d+\)\s+:\s+parsing\s\[.+:(?P<line>\d+)\]\s+:\s+(?P<message>.+)`), | ||
regexp.MustCompile(`\[(?P<severity>\w+)\]\s+\d+\/\d+\s+\(\d+\)\s+:\s+(?P<message>.+)\s+at\s\[.+:(?P<line>\d+)\]\s+.+`), | ||
} | ||
stopWords := regexp.MustCompile(`unable to load SSL private key|file`) | ||
if stopWords.MatchString(line) { | ||
return nil | ||
} | ||
for _, re := range regexps { | ||
matches := re.FindAllStringSubmatch(line, -1) | ||
pos := map[string]int{} | ||
for i, name := range re.SubexpNames() { | ||
pos[name] = i | ||
} | ||
if len(matches) == 1 { | ||
if len(matches[0]) == 4 { | ||
lineNum, err := strconv.Atoi(matches[0][pos["line"]]) | ||
if err != nil { | ||
return nil | ||
} | ||
severity := "critical" | ||
if matches[0][pos["severity"]] == "WARNING" { | ||
severity = "warning" | ||
} | ||
return &Problem{ | ||
Line: lineNum, | ||
Col: 0, | ||
Severity: severity, | ||
Message: matches[0][pos["message"]], | ||
} | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// ParseHaproxyOutput parses whole HAProxy config check output | ||
func ParseHaproxyOutput(f io.Reader) ([]Problem, error) { | ||
b, err := ioutil.ReadAll(f) | ||
if err != nil { | ||
return nil, err | ||
} | ||
var problems []Problem | ||
lines := strings.Split(string(b), "\n") | ||
for _, line := range lines { | ||
p := ParseHaproxyLine(line) | ||
if p != nil { | ||
problems = append(problems, *p) | ||
} | ||
} | ||
return problems, nil | ||
} | ||
|
||
// RunHAProxyCheck executes HAProxy binary in check mode and parses it's output | ||
func RunHAProxyCheck(filePath string) ([]Problem, error) { | ||
cmd := exec.Command("haproxy", "-c", "-f", filePath) | ||
var out bytes.Buffer | ||
cmd.Stderr = &out | ||
err := cmd.Run() | ||
if err != nil { | ||
// haproxy exits with exit code 1 on any problems with config, | ||
// but if we don't have haproxy executable we won't get exit code. | ||
// Here we check it. | ||
if _, ok := err.(*exec.ExitError); !ok { | ||
return nil, err | ||
} | ||
|
||
} | ||
return ParseHaproxyOutput(&out) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package lib | ||
|
||
import ( | ||
"os" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
func TestParseHaproxyLine(t *testing.T) { | ||
tests := []struct { | ||
// Test description. | ||
name string | ||
// Parameters. | ||
line string | ||
// Expected results. | ||
want *Problem | ||
}{ | ||
{ | ||
name: "Warning output", | ||
line: "[WARNING] 098/114746 (5447) : parsing [/tmp/tmp.cfg:42] : a 'redirect' rule placed after a 'use_backend' rule will still be processed before.", | ||
want: &Problem{Line: 42, Col: 0, Severity: "warning", Message: "a 'redirect' rule placed after a 'use_backend' rule will still be processed before."}, | ||
}, | ||
{ | ||
name: "Alert output", | ||
line: "[ALERT] 098/114746 (5447) : parsing [/tmp/tmp.cfg:36] : unknown keyword 'deffault_backend' in 'frontend' section", | ||
want: &Problem{Line: 36, Col: 0, Severity: "critical", Message: "unknown keyword 'deffault_backend' in 'frontend' section"}, | ||
}, | ||
{ | ||
name: "Summary output", | ||
line: "[ALERT] 098/114746 (5447) : Fatal errors found in configuration.", | ||
want: nil, | ||
}, | ||
{ | ||
name: "Bad output", | ||
line: " [ ALL] accept-proxy", | ||
want: nil, | ||
}, | ||
{ | ||
name: "SSL cert output", | ||
line: "[ALERT] 098/131824 (13707) : parsing [/tmp/tmp.cfg:34] : 'bind :443' : unable to load SSL private key from PEM file '/cert/cert.pem'.", | ||
want: nil, | ||
}, | ||
{ | ||
name: "SSL cert problem", | ||
line: "[ALERT] 098/131824 (13707) : Proxy 'secured': no SSL certificate specified for bind ':443' at [/tmp/tmp.cfg:34] (use 'crt').", | ||
want: &Problem{Line: 34, Col: 0, Severity: "critical", Message: "Proxy 'secured': no SSL certificate specified for bind ':443'"}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
if got := ParseHaproxyLine(tt.line); !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("%q. ParseHaproxyLine() = %v, want %v", tt.name, got, tt.want) | ||
} | ||
} | ||
} | ||
|
||
func TestParseHaproxyOutput(t *testing.T) { | ||
f, err := os.Open("../testdata/example_output.txt") | ||
if err != nil { | ||
t.Fatalf("Unexpected error while opening test output file: %v", err) | ||
} | ||
defer f.Close() | ||
problems, err := ParseHaproxyOutput(f) | ||
if err != nil { | ||
t.Fatalf("Unexpected error while parsing test output: %v", err) | ||
} | ||
|
||
if len(problems) != 6 { | ||
t.Errorf("Expected %d problems, got %d", 6, len(problems)) | ||
} | ||
|
||
} |
Oops, something went wrong.