Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP probe: add support for HTTP methods, request payload and status … #47

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,21 @@ probe "probe-name" {
}

http {
method = "post"
scheme = "http"
host = {
hostname = "localhost"
port = 8080
}
path = "/status"
timeout = "5s"
payload = "{\"body\": 123}"
# regex to match against the response status line
# (e.g. 403 Forbidden)
expectStatus = "(200|201)"
headers = {
Content-Type = "application/json"
}
}
}
```
Expand Down
12 changes: 8 additions & 4 deletions internal/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,15 @@ type SMTP struct {
Host
}

type HttpGet struct {
type HTTP struct {
Method string
Scheme string
Host
Path string
Timeout string
Path string
Timeout string
Payload string
ExpectStatus string
Headers map[string]string
}

type Probe struct {
Expand All @@ -58,7 +62,7 @@ type Probe struct {
Redis *Redis
MongoDB *MongoDB
Amqp *Amqp
HTTP *HttpGet
HTTP *HTTP
SMTP *SMTP
}

Expand Down
82 changes: 51 additions & 31 deletions pkg/probe/probe_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,98 @@ package probe

import (
"fmt"
"net"
"net/http"
"net/url"
"regexp"
"strings"
"time"

"github.com/mittwald/mittnite/internal/config"
"github.com/mittwald/mittnite/internal/helper"
log "github.com/sirupsen/logrus"
)

type httpGetProbe struct {
type httpProbe struct {
method string
scheme string
host string
path string
timeout string
payload string
headers map[string]string
timeout time.Duration
status *regexp.Regexp
}

func NewHttpProbe(cfg *config.HttpGet) *httpGetProbe {
cfg.Scheme = helper.ResolveEnv(cfg.Scheme)
func NewHttpProbe(cfg *config.HTTP) (*httpProbe, error) {
cfg.Method = helper.SetDefaultStringIfEmpty(helper.ResolveEnv(cfg.Method), "GET", "method", "http")
cfg.Scheme = helper.SetDefaultStringIfEmpty(helper.ResolveEnv(cfg.Scheme), "http", "scheme", "http")
cfg.Hostname = helper.ResolveEnv(cfg.Hostname)
cfg.Port = helper.ResolveEnv(cfg.Port)
cfg.Path = helper.ResolveEnv(cfg.Path)
cfg.Timeout = helper.ResolveEnv(cfg.Timeout)

if cfg.Scheme == "" {
cfg.Scheme = "http"
}
cfg.Timeout = helper.SetDefaultStringIfEmpty(helper.ResolveEnv(cfg.Timeout), "5s", "timeout", "http")
cfg.ExpectStatus = helper.SetDefaultStringIfEmpty(helper.ResolveEnv(cfg.ExpectStatus), `(1|2|3)\d\d\s`, "expectStatus", "http")

method := strings.ToUpper(cfg.Method)
host := cfg.Hostname
if cfg.Port != "" {
host = fmt.Sprintf("%s:%s", cfg.Hostname, cfg.Port)
host = net.JoinHostPort(cfg.Hostname, cfg.Port)
}

status, err := regexp.Compile(cfg.ExpectStatus)
if err != nil {
return nil, fmt.Errorf("invalid HTTP status line regexp: %w", err)
}

connCfg := httpGetProbe{
timeout, err := time.ParseDuration(cfg.Timeout)
if err != nil {
return nil, fmt.Errorf("invalid timeout duration: %w", err)
}

connCfg := &httpProbe{
method: method,
scheme: cfg.Scheme,
host: host,
path: cfg.Path,
timeout: cfg.Timeout,
status: status,
timeout: timeout,
payload: cfg.Payload,
headers: cfg.Headers,
}

return &connCfg
return connCfg, nil
}

func (h *httpGetProbe) Exec() error {
timeout := time.Second * 5
if h.timeout != "" {
duration, err := time.ParseDuration(h.timeout)
if err == nil {
timeout = duration
} else {
return fmt.Errorf("invalid timeout duration: %s", err)
}
}

func (h *httpProbe) Exec() error {
u := url.URL{
Scheme: h.scheme,
Host: h.host,
Path: h.path,
}
urlStr := u.String()

client := &http.Client{
Timeout: timeout,
Timeout: h.timeout,
}
res, err := client.Get(urlStr)

data := strings.NewReader(h.payload)
req, err := http.NewRequest(h.method, u.String(), data)
if err != nil {
return err
}

for k, v := range h.headers {
req.Header.Set(k, v)
}

res, err := client.Do(req)
if err != nil {
return err
}

if res.StatusCode >= 200 && res.StatusCode < 400 {
log.WithFields(log.Fields{"kind": "probe", "name": "http", "status": "alive", "host": urlStr}).Debug()
return nil
if !h.status.MatchString(res.Status) {
return fmt.Errorf("http service %q returned status %q", urlStr, res.Status)
}

return fmt.Errorf("http service '%s' returned status code %d", urlStr, res.StatusCode)
log.WithFields(log.Fields{"kind": "probe", "name": "http", "status": "alive", "host": urlStr}).Debug()
return nil
}
46 changes: 26 additions & 20 deletions pkg/probe/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,29 +143,15 @@ func filterWaitProbes(cfg *config.Ignition, probes map[string]Probe) map[string]
}

func buildProbesFromConfig(cfg *config.Ignition) (map[string]Probe, error) {
result := make(map[string]Probe)

var errs []error

result := make(map[string]Probe)
for i := range cfg.Probes {
if cfg.Probes[i].Filesystem != "" {
result[cfg.Probes[i].Name] = &filesystemProbe{cfg.Probes[i].Filesystem}
} else if cfg.Probes[i].MySQL != nil {
result[cfg.Probes[i].Name] = NewMySQLProbe(cfg.Probes[i].MySQL)
} else if cfg.Probes[i].Redis != nil {
result[cfg.Probes[i].Name] = NewRedisProbe(cfg.Probes[i].Redis)
} else if cfg.Probes[i].MongoDB != nil {
var err error
result[cfg.Probes[i].Name], err = NewMongoDBProbe(cfg.Probes[i].MongoDB)
if err != nil {
errs = append(errs, err)
}
} else if cfg.Probes[i].Amqp != nil {
result[cfg.Probes[i].Name] = NewAmqpProbe(cfg.Probes[i].Amqp)
} else if cfg.Probes[i].HTTP != nil {
result[cfg.Probes[i].Name] = NewHttpProbe(cfg.Probes[i].HTTP)
} else if cfg.Probes[i].SMTP != nil {
result[cfg.Probes[i].Name] = NewSmtpProbe(cfg.Probes[i].SMTP)
p, err := newProbe(cfg.Probes[i])
if err != nil {
errs = append(errs, err)
} else if p != nil {
result[cfg.Probes[i].Name] = p
}
}

Expand All @@ -176,3 +162,23 @@ func buildProbesFromConfig(cfg *config.Ignition) (map[string]Probe, error) {

return result, err
}

func newProbe(p config.Probe) (Probe, error) {
if p.Filesystem != "" {
return &filesystemProbe{p.Filesystem}, nil
} else if p.MySQL != nil {
return NewMySQLProbe(p.MySQL), nil
} else if p.Redis != nil {
return NewRedisProbe(p.Redis), nil
} else if p.MongoDB != nil {
return NewMongoDBProbe(p.MongoDB)
} else if p.Amqp != nil {
return NewAmqpProbe(p.Amqp), nil
} else if p.HTTP != nil {
return NewHttpProbe(p.HTTP)
} else if p.SMTP != nil {
return NewSmtpProbe(p.SMTP), nil
}

return nil, nil
}