Skip to content

Commit

Permalink
Merge pull request #1 from ph4r5h4d/feature/mysql-check
Browse files Browse the repository at this point in the history
Feature/mysql check
  • Loading branch information
ph4r5h4d authored Feb 9, 2020
2 parents a7b0fc0 + 6737058 commit 05fd4a1
Show file tree
Hide file tree
Showing 14 changed files with 246 additions and 42 deletions.
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
# wait4it
A simple go application to test whether a port is ready to accept connection or not.
A simple go application to test whether a port is ready to accept connection and also you can check whether your
MySQL server is ready or not.
It also supports **timeout** so it can wait for a certain time and then fail.

## Install
You can download the latest release or you can build it yourself.
To build just run `go build -o wait4it`

## Usage
## Command Line Args
The following command line flags are supported

* h (host to check, default is 127.0.0.1)
* p (port to check on the host)
* t (timeout in seconds, time to wait before considering the opration as failed. default is 30)
* u (username for the services that needs username)
* P (password for the services that needs password)
* n (currently this param is used to identify database name for MySQL)

Sample
`./wait4it -h=127.0.0.1 -p=8080 -t=60 `
### Sample
Check a TCP port
```bash
./wait4it -h=127.0.0.1 -p=8080 -t=60
```

Check a MySQL instance
```bash
./wait4it -h=127.0.0.1 -p=3306 -t=60 -u=root -P=secret -n=app
```

## Notes
#### Exit codes
* 0: connection established successfully
* 1: timed out
* 2: mostly means a validation error or something wrong with the input data
50 changes: 50 additions & 0 deletions check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"os"
"time"
"wait4it/checkMySQL"
"wait4it/checkTcp"
)

func ticker(cs interface{}, ct string, t *time.Ticker, d chan bool) {
for {
select {
case <-d:
return
case <-t.C:
check(cs, ct)
}
}
}

func check(cs interface{}, ct string) {
switch ct {
case "tcp":
tcpCheck(cs.(checkTcp.IP))
case "mysql":
mysqlCheck(cs.(checkMySQL.MySQLConnection))
}
}

func tcpCheck(ip checkTcp.IP) {
r, err := ip.DoesPortAcceptConnection()

if err != nil {
wStdErr(err.Error())
os.Exit(2)
}

wStdOut(r)
}

func mysqlCheck(c checkMySQL.MySQLConnection) {
r, err := c.IsMySQLAvailable()

if err != nil {
wStdErr(err.Error())
os.Exit(2)
}

wStdOut(r)
}
38 changes: 38 additions & 0 deletions checkMySQL/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package checkMySQL

import (
"errors"
"strconv"
)

func (m MySQLConnection) BuildConnectionString() string {
dsl := ""

if len(m.Password) == 0 {
dsl = dsl + m.Username
}else{
dsl = dsl + m.Username + ":" + m.Password
}

dsl = dsl + "@tcp(" + m.Host + ":" + strconv.Itoa(m.Port) + ")/"

if len(m.DatabaseName) > 0 {
dsl = dsl + m.DatabaseName
}

return dsl
}

func (m MySQLConnection) validateStruct() error{
if len(m.Host) == 0 || len(m.Username) == 0 {
return errors.New("host or username can't be empty")
}

if m.Port < 0 || m.Port > 65535 {
return errors.New("invalid port range for mysql")
}

return nil
}


32 changes: 32 additions & 0 deletions checkMySQL/mysqlChecker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package checkMySQL

import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)

func (m MySQLConnection) IsMySQLAvailable() (bool, error) {
err := m.validateStruct()

if err != nil {
return false, err
}

dsl := m.BuildConnectionString()

db, err := sql.Open("mysql", dsl)

// if there is an error opening the connection, handle it
if err != nil {
return false, err
}

err = db.Ping()
if err != nil {
// todo: need a logger
return false, nil
}

_ = db.Close()
return true, nil
}
9 changes: 9 additions & 0 deletions checkMySQL/structs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package checkMySQL

type MySQLConnection struct {
Host string
Port int
Username string
Password string
DatabaseName string
}
2 changes: 1 addition & 1 deletion tcpcheck/helpers.go → checkTcp/helpers.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tcpcheck
package checkTcp

func (i IP) isPortInValidRange() bool {
if i.Port < minPort || i.Port > maxPort {
Expand Down
2 changes: 1 addition & 1 deletion tcpcheck/structs.go → checkTcp/structs.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tcpcheck
package checkTcp

const (
minPort = 1
Expand Down
2 changes: 1 addition & 1 deletion tcpcheck/tcpChecker.go → checkTcp/tcpChecker.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tcpcheck
package checkTcp

import (
"errors"
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module wait4it

go 1.13

require github.com/go-sql-driver/mysql v1.5.0
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
55 changes: 20 additions & 35 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,42 @@ import (
"fmt"
"os"
"time"
"wait4it/tcpcheck"
)

func main() {
ip := flag.String("h", "127.0.0.1", "IP of the host you want to test against")
port := flag.Int("p", 0, "Port")
ct := flag.String("type", "tcp", "define the type of check, currently [tcp,mysql] are supported")
timeout := flag.Int("t", 30, "Timeout, amount of time wait4it waits for the port in seconds")
host := flag.String("h", "127.0.0.1", "IP of the host you want to test against")
port := flag.Int("p", 0, "Port")
username := flag.String("u", "", "Username of the service")
password := flag.String("P", "", "Password of the service")
databaseName := flag.String("n", "", "Name of the database")

flag.Parse()

i := tcpcheck.IP{
Addr: *ip,
Port: *port,
c := checkContext{
config: configurationContext{
checkType: *ct,
timeout: *timeout,
},
Host: *host,
Port: *port,
Username: *username,
Password: *password,
DatabaseName: *databaseName,
}
fmt.Println(fmt.Sprintf("Starting to check on %s on port %d with %d as timeout", *ip, *port, *timeout))
cs := c.getStructByCheckType()

fmt.Print("Wait4it...")

t := time.NewTicker(1 * time.Second)
done := make(chan bool)

go ticker(i, t, done)
go ticker(cs, c.config.checkType, t, done)

time.Sleep(time.Duration(*timeout) * time.Second)
done <- true

fmt.Print("failed")
os.Exit(1)
}

func ticker(ip tcpcheck.IP, t *time.Ticker, d chan bool) {
for {
select {
case <-d:
return
case <-t.C:
check(ip)
}
}
}

func check(ip tcpcheck.IP) {
r, err := ip.DoesPortAcceptConnection()

if err != nil {
fmt.Println(err.Error())
os.Exit(2)
}

if r {
fmt.Println("succeed")
os.Exit(0)
} else {
fmt.Print(".")
}
}
19 changes: 19 additions & 0 deletions output.std.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"fmt"
"os"
)

func wStdOut(r bool) {
if r {
_, _ = fmt.Fprint(os.Stdout, "succeed")
os.Exit(0)
} else {
fmt.Fprint(os.Stdout, ".")
}
}

func wStdErr(a ...interface{}){
_, _ = fmt.Fprint(os.Stderr, a)
}
15 changes: 15 additions & 0 deletions structs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

type checkContext struct {
config configurationContext
Host string
Port int
Username string
Password string
DatabaseName string
}

type configurationContext struct {
checkType string
timeout int
}
34 changes: 34 additions & 0 deletions type-parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import (
"wait4it/checkMySQL"
"wait4it/checkTcp"
)

func (c checkContext) getStructByCheckType() interface{} {
switch c.config.checkType {
case "tcp":
return buildIpCheckStruct(c)
case "mysql":
return buildMySQLCheckStruct(c)
}

return nil
}

func buildIpCheckStruct(c checkContext) checkTcp.IP {
return checkTcp.IP{
Addr: c.Host,
Port: c.Port,
}
}

func buildMySQLCheckStruct(c checkContext) checkMySQL.MySQLConnection {
return checkMySQL.MySQLConnection{
Host: c.Host,
Port: c.Port,
Username: c.Username,
Password: c.Password,
DatabaseName: c.DatabaseName,
}
}

0 comments on commit 05fd4a1

Please sign in to comment.