Skip to content

Commit

Permalink
First release
Browse files Browse the repository at this point in the history
  • Loading branch information
larrabee committed Sep 1, 2018
1 parent f1eb5c1 commit 8af21c1
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# IDE
.idea/
pxc-checker
config/test.conf
24 changes: 24 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
builds:
-
env:
- CGO_ENABLED=0
binary: pxc-checker
archive:
replacements:
darwin: MacOS
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
20 changes: 20 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
language: go
go:
- 1.9.x
- tip

os:
- linux
dist: trusty
sudo: false

matrix:
allow_failures:
- go: tip

gobuild_args: -v

script:
- cd "${TRAVIS_BUILD_DIR}"
- go get
- go build -o pxc-checker ./...
104 changes: 104 additions & 0 deletions checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package main

import (
"encoding/json"
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/valyala/fasthttp"
"strconv"
"time"
)

type ReasonCode int

const (
reasonInternalError ReasonCode = -1
reasonOk ReasonCode = 0
reasonForceEnabled ReasonCode = 1
reasonNodeNotAvailable ReasonCode = 2
reasonWSRepFailed ReasonCode = 3
reasonCheckTimeout ReasonCode = 4
reasonRWDisabled ReasonCode = 5
)

type Response struct {
*NodeStatus
ReasonText string
ReasonCode ReasonCode
}

func checkerHandler(ctx *fasthttp.RequestCtx) {

response := Response{NodeStatus: status}
ctx.SetContentType("application/json")

if config.CheckForceEnable {
ctx.SetStatusCode(fasthttp.StatusOK)
response.ReasonText = "Force enabled"
response.ReasonCode = reasonForceEnabled
} else if !status.NodeAvailable {
ctx.SetStatusCode(fasthttp.StatusServiceUnavailable)
response.ReasonText = "Node is not available"
response.ReasonCode = reasonNodeNotAvailable
} else if (status.WSRepStatus != 4) && (status.WSRepStatus != 2) {
ctx.SetStatusCode(fasthttp.StatusServiceUnavailable)
response.ReasonText = "WSRep failed"
response.ReasonCode = reasonWSRepFailed
} else if float64(status.Timestamp)+float64(config.CheckFailTimeout)/1000 < float64(time.Now().Unix()) {
ctx.SetStatusCode(fasthttp.StatusServiceUnavailable)
response.ReasonText = "Check timeout"
response.ReasonCode = reasonCheckTimeout
} else if !config.CheckROEnabled && !status.RWEnabled {
ctx.SetStatusCode(fasthttp.StatusServiceUnavailable)
response.ReasonText = "Node is read only"
response.ReasonCode = reasonRWDisabled
} else {
ctx.SetStatusCode(fasthttp.StatusOK)
response.ReasonText = "OK"
response.ReasonCode = reasonOk
}

if respJson, err := json.Marshal(response); err != nil {
errStr := fmt.Sprintf(`{"ReasonText":"Internal checker error","ReasonCode":%d,"err":"%s"}`, reasonInternalError, err)
ctx.SetBody([]byte(errStr))
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
} else {
ctx.SetBody(respJson)
}

return
}

func checker(status *NodeStatus) {
for {
time.Sleep(time.Duration(config.CheckInterval) * time.Millisecond)
curStatus := &NodeStatus{}
curStatus.Timestamp = int(time.Now().Unix())

rows, err := dbConn.Query("SHOW GLOBAL VARIABLES;")
if err != nil {
*status = *curStatus
continue
}
curStatus.NodeAvailable = true

for rows.Next() {
var key, value string
err := rows.Scan(&key, &value)
if err != nil {
*status = *curStatus
continue
}
switch key {
case "read_only":
if value == "OFF" {
curStatus.RWEnabled = true
}
case "wsrep_local_state":
curStatus.WSRepStatus, _ = strconv.Atoi(value)
}
}

*status = *curStatus
}
}
11 changes: 11 additions & 0 deletions config/example.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
WEB_LISTEN=":9200"

MYSQL_HOST="127.0.0.1"
MYSQL_PORT=3306
MYSQL_USER="monitor"
MYSQL_PASS="monitorUserPassword"

CHECK_RO_ENABLED=false
CHECK_FORCE_ENABLE=false
CHECK_INTERVAL=500 # ms
CHECK_FAIL_TIMEOUT=3000 # ms
91 changes: 91 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"database/sql"
"fmt"
"github.com/buaazp/fasthttprouter"
"github.com/labstack/gommon/log"
"github.com/namsral/flag"
"github.com/valyala/fasthttp"
"time"
)

type NodeStatus struct {
WSRepStatus int
RWEnabled bool
NodeAvailable bool
Timestamp int
}

type Config struct {
WebListen string
WebReadTimeout int
WebWriteTimeout int
CheckROEnabled bool
CheckInterval int
CheckFailTimeout int
CheckForceEnable bool
MysqlHost string
MysqlPort int
MysqlUser string
MysqlPass string
MysqlTimeout int
}

var (
status = &NodeStatus{}
config *Config
dbConn *sql.DB
)

func main() {
var err error
config, err = parseFlags()
if err != nil {
log.Fatalf("Options parsing failed with err: %s", err)
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/", config.MysqlUser, config.MysqlPass, config.MysqlHost, config.MysqlPort)
log.Printf("Connecting to mysql with dsn: %s", dsn)
dbConn, _ = sql.Open("mysql", dsn)

go checker(status)

router := getRouter()
server := &fasthttp.Server{
Handler: router.Handler,
DisableKeepalive: true,
GetOnly: true,
Concurrency: 2048,
ReadTimeout: time.Duration(config.WebReadTimeout) * time.Millisecond,
WriteTimeout: time.Duration(config.WebWriteTimeout) * time.Millisecond,
}

log.Printf("Server starting on %s", config.WebListen)
if err := server.ListenAndServe(config.WebListen); err != nil {
log.Fatalf("Error in ListenAndServe: %s", err)
}
}

func getRouter() *fasthttprouter.Router {
router := fasthttprouter.New()
router.GET("/", checkerHandler)
return router
}

func parseFlags() (*Config, error) {
config := Config{}
flag.StringVar(&config.WebListen, "WEB_LISTEN", ":9200", "Listen interface and port")
flag.IntVar(&config.WebReadTimeout, "WEB_READ_TIMEOUT", 30000, "Request read timeout, ms")
flag.IntVar(&config.WebWriteTimeout, "WEB_WRITE_TIMEOUT", 30000, "Request write timeout, ms")
flag.BoolVar(&config.CheckROEnabled, "CHECK_RO_ENABLED", false, "Make 'read_only' nodes availible")
flag.BoolVar(&config.CheckForceEnable, "CHECK_FORCE_ENABLE", false, "Ignoring checks status and force enable node")
flag.IntVar(&config.CheckInterval, "CHECK_INTERVAL", 500, "Mysql checks interval, ms")
flag.IntVar(&config.CheckFailTimeout, "CHECK_FAIL_TIMEOUT", 3000, "To count a node inaccessible if for the specified time there were no successful checks, ms")
flag.StringVar(&config.MysqlHost, "MYSQL_HOST", "127.0.0.1", "MySQL host addr")
flag.IntVar(&config.MysqlPort, "MYSQL_PORT", 3306, "MySQL port addr")
flag.StringVar(&config.MysqlUser, "MYSQL_USER", "monitor", "MySQL username")
flag.StringVar(&config.MysqlPass, "MYSQL_PASS", "", "MySQL password")

flag.Parse()
return &config, nil
}
13 changes: 13 additions & 0 deletions systemd/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Description=PXC Checker
After=network.target

[Service]
EnvironmentFile=/etc/pxc/checker/%i.conf
ExecStart=/usr/bin/pxc-checker
KillMode=process
User=mysql
Group=mysql

[Install]
WantedBy=multi-user.target

0 comments on commit 8af21c1

Please sign in to comment.