Skip to content

Commit

Permalink
Merge pull request #7 from ph4r5h4d/http-support
Browse files Browse the repository at this point in the history
Add Http support
  • Loading branch information
ph4r5h4d authored Feb 18, 2020
2 parents a76da99 + 4d9960d commit 39f000c
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 3 deletions.
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# wait4it
A simple go application to test whether a port is ready to accept a connection, and also you can check whether your
MySQL or PostgresQL server is ready or not.

![LICENSE](https://img.shields.io/github/workflow/status/ph4r5h4d/wait4it/Wait4it CI?style=for-the-badge) [![Docker Pull](https://img.shields.io/docker/pulls/ph4r5h4d/wait4it?style=for-the-badge)](https://hub.docker.com/r/ph4r5h4d/wait4it) ![GO Version](https://img.shields.io/github/go-mod/go-version/ph4r5h4d/wait4it?style=for-the-badge) ![TAG](https://img.shields.io/github/v/tag/ph4r5h4d/wait4it?style=for-the-badge) ![LICENSE](https://img.shields.io/github/license/ph4r5h4d/wait4it?style=for-the-badge)

A simple go application to test whether a port is ready to accept a connection or check
MySQL or PostgreSQL server is ready or not, Also you can do Http call and check the response code and text in response.
It also supports **timeout** so it can wait for a particular time and then fail.

## Supported Modules
* TCP port
* MySQL
* PostgresQL
* Http

## Install
You can download the latest release, or you can build it yourself.
You can download the latest [release](https://github.com/ph4r5h4d/wait4it/releases), or you can build it yourself.
To build just run `go build -o wait4it`

## Command Line Args
Expand All @@ -21,6 +25,9 @@ The following command-line flags are supported
* 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)
* ssl (whether to enable or disable ssl-mode for Postgres)
* http-status (for Http check, which status code to expect)
* http-text (for Http check, find substring inside the response)

### Sample
Check a TCP port
Expand All @@ -37,6 +44,11 @@ Check a PostgresQL instance
./wait4it -type=postgres -h=127.0.0.1 -p=5432 -t=60 -u=postgres -P=secret -ssl=disable
```

Check HTTP response and text
```bash
-type=http -h=https://farshad.nematdoust.com -t=60 -status-code=200 -http-text="Software Engineer"
```

### Docker
You can run this `wait4it` inside a docker container, and it's possible to run this container as init container inside
K8s and Openshift.
Expand All @@ -61,8 +73,20 @@ Check a PostgresQL instance
docker run ph4r5h4d/wait4it -type=postgres -h=127.0.0.1 -p=5432 -t=60 -u=postgres -P=secret -ssl=disable
```

Check HTTP response and text
```bash
docker run ph4r5h4d/wait4it -type=http -h=https://farshad.nematdoust.com -t=60 -status-code=200 -http-text="Software Engineer"
```

## Notes
#### Exit codes
* 0: connection established successfully
* 1: timed out
* 2: mostly means a validation error or something wrong with the input data

#### Http check
* for the Http check if you do not define status code it will check for 200 status code
* if `http-text` is not defined then wait4it will check the status code

#### Postgres check
* if `ssl` is not defined then it's `disable` by default
2 changes: 2 additions & 0 deletions cmd/check-module-list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import (
"wait4it/MySQLChecker"
"wait4it/PostgreSQLChecker"
"wait4it/TcpChecker"
"wait4it/httpChecker"
)

var cm = map[string]interface{}{
"tcp": &TcpChecker.Tcp{},
"mysql": &MySQLChecker.MySQLConnection{},
"postgres": &PostgreSQLChecker.PostgresSQLConnection{},
"http": &httpChecker.HttpCheck{},
}
25 changes: 25 additions & 0 deletions httpChecker/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package httpChecker

import "net/url"

func (h *HttpCheck) validateUrl() bool {
_, err := url.ParseRequestURI(h.Url)
if err != nil {
return false
}

u, err := url.Parse(h.Url)
if err != nil || u.Scheme == "" || u.Host == "" {
return false
}

return true
}

func (h *HttpCheck) validateStatusCode() bool {
// check against common status code
if h.Status < 100 || h.Status > 599 {
return false
}
return true
}
54 changes: 54 additions & 0 deletions httpChecker/httpChecker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package httpChecker

import (
"errors"
"io/ioutil"
"net/http"
"strings"
"wait4it/model"
)

func (h *HttpCheck) BuildContext(cx model.CheckContext) {
h.Url = cx.Host
h.Status = cx.HttpConf.StatusCode
if len(cx.HttpConf.Text) > 0 {
h.Text = cx.HttpConf.Text
}
}

func (h *HttpCheck) Validate() (bool, error) {
if !h.validateUrl() {
return false, errors.New("invalid URL provided")
}

if !h.validateStatusCode() {
return false, errors.New("invalid status code provided")
}

return true, nil
}

func (h *HttpCheck) Check() (bool, bool, error) {
resp, err := http.Get(h.Url)

if err != nil {
return false, true, err
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return false, true, err
}

if resp.StatusCode != h.Status {
return false, false, errors.New("invalid status code")
}

if len(h.Text) > 0 {
if !strings.Contains(string(body), h.Text) {
return false, false, errors.New("can't find substring in response")
}
}

return true, false, nil
}
7 changes: 7 additions & 0 deletions httpChecker/structs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package httpChecker

type HttpCheck struct {
Url string
Status int
Text string
}
6 changes: 6 additions & 0 deletions inputParser/input-parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ func GetInput() model.CheckContext {
password := flag.String("P", "", "Password of the service")
databaseName := flag.String("n", "", "Name of the database")
sslMode := flag.String("ssl", "disable", "Enable or Disable ssl mode (for some database or services)")
httpCode := flag.Int("status-code", 200, "Status code to be expected from http call")
httpText := flag.String("http-text", "", "Text to check inside http response")
flag.Parse()

c := model.CheckContext{
Expand All @@ -29,6 +31,10 @@ func GetInput() model.CheckContext {
DBConf: model.DatabaseSpecificConf{
SSLMode: *sslMode,
},
HttpConf: model.HttpSpecificConf{
StatusCode: *httpCode,
Text: *httpText,
},
}
return c
}
5 changes: 5 additions & 0 deletions model/check-context-struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type CheckContext struct {
Password string
DatabaseName string
DBConf DatabaseSpecificConf
HttpConf HttpSpecificConf
}

type ConfigurationContext struct {
Expand All @@ -18,3 +19,7 @@ type ConfigurationContext struct {
type DatabaseSpecificConf struct {
SSLMode string
}
type HttpSpecificConf struct {
StatusCode int
Text string
}

0 comments on commit 39f000c

Please sign in to comment.