-
Notifications
You must be signed in to change notification settings - Fork 339
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add space requirements to RECOVERY.md * Ensure correct permission for ssh keys even if daemon is not enabled * Barman doesn't require running PG to start * Add .swp to gitignore * Add metrics for Barman * Add prefix to metrics. Updated README.md
- Loading branch information
Showing
13 changed files
with
493 additions
and
21 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
.idea | ||
*.swp |
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,16 @@ | ||
## Metrics | ||
|
||
Metrics are exposed in prometheus format on `:8080/metrics` via small go deamon which collects data from `fs`, `barman diagnose` and `barman check` commands. | ||
|
||
Type of all metrics is GAUGE. | ||
Available metrics are: | ||
* `barman_check_is_ok` 0 or 1. 1 means ok and returned when exit code of `barman check` is 0 | ||
* `barman_backups_amount` | ||
* `barman_last_backup_start_time_seconds` | ||
* `barman_last_backup_end_time_seconds` | ||
* `barman_last_backup_size_bytes` | ||
* `barman_last_backup_duration_total_seconds` | ||
* `barman_last_backup_duration_copy_seconds` | ||
* `barman_oldest_backup_end_time_seconds` | ||
* `barman_backup_disk_free_bytes` | ||
* `barman_backup_disk_used_bytes` |
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,12 @@ | ||
#!/usr/bin/env bash | ||
set -e | ||
|
||
SLOTS_COUNT=`barman show-server $UPSTREAM_NAME | grep "replication_slot: Record(slot_name='$REPLICATION_SLOT_NAME'" | wc -l` | ||
if [ "$SLOTS_COUNT" -gt "0" ]; then | ||
echo "Looks like replication slot already exists" | ||
else | ||
echo "Creating replication slot: $REPLICATION_SLOT_NAM" | ||
barman receive-wal --create-slot $UPSTREAM_NAME | ||
fi | ||
|
||
barman cron |
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 |
---|---|---|
@@ -1,2 +1,2 @@ | ||
* * * * * root barman cron > /proc/1/fd/1 2> /proc/1/fd/2 | ||
*/5 * * * * root cd /home/postgres && /usr/local/bin/barman_docker/wal-receiver.sh > /proc/1/fd/1 2> /proc/1/fd/2 | ||
|
Binary file not shown.
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,131 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"os/exec" | ||
"encoding/json" | ||
// "github.com/davecgh/go-spew/spew" | ||
"syscall" | ||
"time" | ||
"strings" | ||
"sort" | ||
) | ||
|
||
func main() { | ||
http.HandleFunc("/metrics", handler) | ||
http.ListenAndServe(":8080", nil) | ||
fmt.Printf("Server is listeinging on :8080. Metrics URL — /metrics") | ||
} | ||
|
||
func handler(w http.ResponseWriter, r *http.Request) { | ||
for k, v := range collectMetrics() { | ||
fmt.Fprintf(w, "%s %d\n", k, v) | ||
} | ||
} | ||
|
||
func collectMetrics() map[string]int64 { | ||
metrics := make(map[string]int64) | ||
|
||
diagnose := barmanDiagnose() | ||
backups := diagnose.Servers.Pg_cluster.Backups | ||
|
||
metrics["barman_check_is_ok"] = int64(barmanCheck()) | ||
metrics["barman_backups_amount"] = int64(len(backups)) | ||
if (len(backups) > 0) { | ||
var keys []string | ||
for k := range backups { | ||
keys = append(keys, k) | ||
} | ||
sort.Strings(keys) | ||
latestBackup := backups[keys[len(keys)-1]] | ||
oldestBackup := backups[keys[0]] | ||
metrics["barman_last_backup_start_time_seconds"] = latestBackup.Begin_time.Unix() | ||
metrics["barman_last_backup_end_time_seconds"] = latestBackup.End_time.Unix() | ||
metrics["barman_last_backup_size_bytes"] = latestBackup.Size | ||
metrics["barman_last_backup_duration_total_seconds"] = int64(latestBackup.Copy_stats["total_time"]) | ||
metrics["barman_last_backup_duration_copy_seconds"] = int64(latestBackup.Copy_stats["copy_time"]) | ||
metrics["barman_oldest_backup_end_time_seconds"] = oldestBackup.End_time.Unix() | ||
} | ||
|
||
var diskUsage syscall.Statfs_t | ||
syscall.Statfs("/var/backups", &diskUsage) | ||
metrics["barman_disk_free_bytes"] = int64(diskUsage.Bavail * uint64(diskUsage.Bsize)) | ||
metrics["barman_disk_used_bytes"] = int64((diskUsage.Blocks - diskUsage.Bfree) * uint64(diskUsage.Bsize)) | ||
|
||
return metrics | ||
} | ||
|
||
var execCommand = exec.Command | ||
func barmanCheck() int { | ||
checkCmd := execCommand("barman", "check") | ||
checkErr := checkCmd.Run() | ||
|
||
barman_check_ok := 1 | ||
if checkErr != nil { | ||
barman_check_ok = 0 | ||
} | ||
|
||
return barman_check_ok | ||
} | ||
|
||
type BarmanDiagnose struct { | ||
Servers struct { | ||
Pg_cluster struct { | ||
Backups map[string]BarmanBackup | ||
} | ||
} | ||
} | ||
|
||
type BarmanBackup struct { | ||
Backup_id string | ||
Begin_time CustomTime | ||
End_time CustomTime | ||
Copy_stats map[string]float64 | ||
Size int64 | ||
} | ||
|
||
func barmanDiagnose() BarmanDiagnose { | ||
cmd := execCommand("barman", "diagnose") | ||
result, err := cmd.Output() | ||
if err != nil { | ||
panic(string(result)) | ||
} | ||
|
||
var diag BarmanDiagnose | ||
jsonErr := json.Unmarshal(result, &diag) | ||
if jsonErr != nil { | ||
panic(jsonErr) | ||
} | ||
|
||
return diag | ||
} | ||
|
||
type CustomTime struct { | ||
time.Time | ||
} | ||
|
||
const ctLayout = "Mon Jan 2 15:04:05 2006" | ||
|
||
func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) { | ||
s := strings.Trim(string(b), "\"") | ||
if s == "null" { | ||
ct.Time = time.Time{} | ||
return | ||
} | ||
ct.Time, err = time.Parse(ctLayout, s) | ||
return | ||
} | ||
|
||
func (ct *CustomTime) MarshalJSON() ([]byte, error) { | ||
if ct.Time.UnixNano() == nilTime { | ||
return []byte("null"), nil | ||
} | ||
return []byte(fmt.Sprintf("\"%s\"", ct.Time.Format(ctLayout))), nil | ||
} | ||
|
||
var nilTime = (time.Time{}).UnixNano() | ||
func (ct *CustomTime) IsSet() bool { | ||
return ct.UnixNano() != nilTime | ||
} | ||
|
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,114 @@ | ||
package main | ||
|
||
import "testing" | ||
import "fmt" | ||
import "os" | ||
import "github.com/stretchr/testify/assert" | ||
import "github.com/stretchr/testify/suite" | ||
import "net/http" | ||
import "net/http/httptest" | ||
import "os/exec" | ||
import "io/ioutil" | ||
import "strconv" | ||
|
||
var fakeExitCode = 0 | ||
|
||
type MainTestSuite struct { | ||
suite.Suite | ||
testHandler http.HandlerFunc | ||
rr *httptest.ResponseRecorder | ||
req *http.Request | ||
} | ||
|
||
func TestAll(t *testing.T) { | ||
execCommand = fakeExecCommand | ||
suite.Run(t, new(MainTestSuite)) | ||
} | ||
|
||
func (suite *MainTestSuite) SetupTest() { | ||
fakeExitCode = 0 | ||
var err error | ||
suite.req, err = http.NewRequest("GET", "http://localhost:8080/metrics", nil) | ||
if err != nil { | ||
panic(err.Error()) | ||
} | ||
|
||
suite.rr = httptest.NewRecorder() | ||
suite.testHandler = http.HandlerFunc(handler) | ||
} | ||
|
||
func (suite *MainTestSuite) TestBarmanCheckFailed() { | ||
fakeExitCode = 1 | ||
suite.testHandler.ServeHTTP(suite.rr, suite.req) | ||
|
||
if status := suite.rr.Code; status != http.StatusOK { | ||
suite.T().Errorf("handler returned wrong status code: got %v want %v", | ||
status, http.StatusOK) | ||
} | ||
|
||
resp := suite.rr.Body.String(); | ||
assert.Contains(suite.T(), resp, "barman_check_is_ok 0") | ||
} | ||
|
||
func (suite *MainTestSuite) TestHandler() { | ||
suite.testHandler.ServeHTTP(suite.rr, suite.req) | ||
|
||
if status := suite.rr.Code; status != http.StatusOK { | ||
suite.T().Errorf("handler returned wrong status code: got %v want %v", | ||
status, http.StatusOK) | ||
} | ||
|
||
resp := suite.rr.Body.String(); | ||
assert.Contains(suite.T(), resp, "barman_check_is_ok 1") | ||
assert.Contains(suite.T(), resp, "barman_backups_amount 2") | ||
assert.Contains(suite.T(), resp, "barman_last_backup_start_time_seconds 1503656945") | ||
assert.Contains(suite.T(), resp, "barman_last_backup_end_time_seconds 1503656955") | ||
assert.Contains(suite.T(), resp, "barman_last_backup_size_bytes 36304273") | ||
assert.Contains(suite.T(), resp, "barman_last_backup_duration_copy_seconds 5") | ||
assert.Contains(suite.T(), resp, "barman_last_backup_duration_total_seconds 10") | ||
assert.Contains(suite.T(), resp, "barman_oldest_backup_end_time_seconds 1503570545") | ||
assert.Contains(suite.T(), resp, "barman_disk_free_bytes ") | ||
assert.Contains(suite.T(), resp, "barman_disk_used_bytes ") | ||
} | ||
|
||
func fakeExecCommand(command string, args...string) *exec.Cmd { | ||
cs := []string{"-test.run=TestHelperProcess", "--", command} | ||
cs = append(cs, args...) | ||
cmd := exec.Command(os.Args[0], cs...) | ||
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1", "GO_FAKE_EXIT_CODE=" + strconv.Itoa(fakeExitCode)} | ||
return cmd | ||
} | ||
|
||
func TestHelperProcess(t *testing.T){ | ||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { | ||
return | ||
} | ||
command := os.Args[3] | ||
arguments := os.Args[4:] | ||
|
||
switch command { | ||
case "barman": | ||
switch arguments[0] { | ||
case "diagnose": | ||
testfile, err := ioutil.ReadFile("test.json") | ||
if err != nil { | ||
panic(err.Error()) | ||
} | ||
fmt.Fprintf(os.Stdout, string(testfile)) | ||
case "check": | ||
code, err := strconv.Atoi(os.Getenv("GO_FAKE_EXIT_CODE")) | ||
if err != nil { | ||
panic(err.Error()) | ||
} | ||
os.Exit(code) | ||
default: | ||
fmt.Fprintf(os.Stderr, "Unknown barman command call") | ||
os.Exit(1) | ||
} | ||
default: | ||
fmt.Fprintf(os.Stderr, "Unknown command call") | ||
os.Exit(2) | ||
} | ||
|
||
os.Exit(0) | ||
} |
Oops, something went wrong.