Skip to content

Commit

Permalink
Version API + never return json arrays as top level
Browse files Browse the repository at this point in the history
  • Loading branch information
mcorbin committed Jul 5, 2024
1 parent 5c8cf99 commit b225d3a
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 30 deletions.
2 changes: 1 addition & 1 deletion exporter/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (c *Component) Start() error {
for message := range c.ChanResult {
c.MemoryStore.Add(message)
if message.Success {
c.Logger.Info("Healthcheck successful",
c.Logger.Debug("Healthcheck successful",
zap.String("name", message.Name),
zap.Reflect("labels", message.Labels),
zap.Int64("healthcheck-timestamp", message.HealthcheckTimestamp),
Expand Down
48 changes: 31 additions & 17 deletions http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ import (
"github.com/mcorbin/corbierror"
)

type ListResultsOutput struct {
Result []healthcheck.Result `json:"result"`
}

type ListHealthchecksOutput struct {
Result []healthcheck.Healthcheck `json:"result"`
}

// BasicResponse a type for HTTP responses
type BasicResponse struct {
Messages []string `json:"messages"`
Expand Down Expand Up @@ -101,8 +109,9 @@ func (c *Component) handlers() {
return corbierror.New("Not found", corbierror.NotFound, true)
}
var bulkLock sync.RWMutex
apiGroup := c.Server.Group("/api/v1")
if !c.Config.DisableHealthcheckAPI {
c.Server.POST("/healthcheck/dns", func(ec echo.Context) error {
apiGroup.POST("/healthcheck/dns", func(ec echo.Context) error {
var config healthcheck.DNSHealthcheckConfiguration
if err := ec.Bind(&config); err != nil {
msg := fmt.Sprintf("Fail to create the dns healthcheck. Invalid JSON: %s", err.Error())
Expand All @@ -117,7 +126,7 @@ func (c *Component) handlers() {
return c.handleCheck(ec, healthcheck)
})

c.Server.POST("/healthcheck/tcp", func(ec echo.Context) error {
apiGroup.POST("/healthcheck/tcp", func(ec echo.Context) error {
var config healthcheck.TCPHealthcheckConfiguration
if err := ec.Bind(&config); err != nil {
msg := fmt.Sprintf("Fail to create the TCP healthcheck. Invalid JSON: %s", err.Error())
Expand All @@ -132,7 +141,7 @@ func (c *Component) handlers() {
return c.handleCheck(ec, healthcheck)
})

c.Server.POST("/healthcheck/tls", func(ec echo.Context) error {
apiGroup.POST("/healthcheck/tls", func(ec echo.Context) error {
var config healthcheck.TLSHealthcheckConfiguration
if err := ec.Bind(&config); err != nil {
msg := fmt.Sprintf("Fail to create the TLS healthcheck. Invalid JSON: %s", err.Error())
Expand All @@ -147,7 +156,7 @@ func (c *Component) handlers() {
return c.handleCheck(ec, healthcheck)
})

c.Server.POST("/healthcheck/http", func(ec echo.Context) error {
apiGroup.POST("/healthcheck/http", func(ec echo.Context) error {
var config healthcheck.HTTPHealthcheckConfiguration
if err := ec.Bind(&config); err != nil {
msg := fmt.Sprintf("Fail to create the HTTP healthcheck. Invalid JSON: %s", err.Error())
Expand All @@ -162,7 +171,7 @@ func (c *Component) handlers() {
return c.handleCheck(ec, healthcheck)
})

c.Server.POST("/healthcheck/command", func(ec echo.Context) error {
apiGroup.POST("/healthcheck/command", func(ec echo.Context) error {
var config healthcheck.CommandHealthcheckConfiguration
if err := ec.Bind(&config); err != nil {
msg := fmt.Sprintf("Fail to create the Command healthcheck. Invalid JSON: %s", err.Error())
Expand All @@ -177,7 +186,7 @@ func (c *Component) handlers() {
return c.handleCheck(ec, healthcheck)
})

c.Server.POST("/healthcheck/bulk", func(ec echo.Context) error {
apiGroup.POST("/healthcheck/bulk", func(ec echo.Context) error {
bulkLock.Lock()
defer bulkLock.Unlock()
var payload BulkPayload
Expand Down Expand Up @@ -244,10 +253,12 @@ func (c *Component) handlers() {
return ec.JSON(http.StatusCreated, newResponse("Healthchecks successfully added"))
})

c.Server.GET("/healthcheck", func(ec echo.Context) error {
return ec.JSON(http.StatusOK, c.healthcheck.ListChecks())
apiGroup.GET("/healthcheck", func(ec echo.Context) error {
return ec.JSON(http.StatusOK, ListHealthchecksOutput{
Result: c.healthcheck.ListChecks(),
})
})
c.Server.GET("/healthcheck/:name", func(ec echo.Context) error {
apiGroup.GET("/healthcheck/:name", func(ec echo.Context) error {
name := ec.Param("name")
healthcheck := c.healthcheck.GetCheck(name)
if healthcheck == nil {
Expand All @@ -256,7 +267,7 @@ func (c *Component) handlers() {
return ec.JSON(http.StatusOK, healthcheck)
})

c.Server.DELETE("/healthcheck/:name", func(ec echo.Context) error {
apiGroup.DELETE("/healthcheck/:name", func(ec echo.Context) error {
name := ec.Param("name")
c.Logger.Info(fmt.Sprintf("Deleting healthcheck %s", name))
err := c.healthcheck.RemoveCheck(name)
Expand All @@ -267,11 +278,14 @@ func (c *Component) handlers() {
return ec.JSON(http.StatusOK, newResponse(fmt.Sprintf("Successfully deleted healthcheck %s", name)))
})
}

if !c.Config.DisableResultAPI {
c.Server.GET("/result", func(ec echo.Context) error {
return ec.JSON(http.StatusOK, c.MemoryStore.List())
apiGroup.GET("/result", func(ec echo.Context) error {
return ec.JSON(http.StatusOK, ListResultsOutput{
Result: c.MemoryStore.List(),
})
})
c.Server.GET("/result/:name", func(ec echo.Context) error {
apiGroup.GET("/result/:name", func(ec echo.Context) error {
name := ec.Param("name")
result, err := c.MemoryStore.Get(name)
if err != nil {
Expand All @@ -280,12 +294,12 @@ func (c *Component) handlers() {
return ec.JSON(http.StatusOK, result)

})
c.Server.GET("/frontend", func(ec echo.Context) error {
err := ec.Redirect(http.StatusFound, "/frontend/index.html")
apiGroup.GET("/frontend", func(ec echo.Context) error {
err := ec.Redirect(http.StatusFound, "/api/v1/frontend/index.html")
return err
})
c.Server.GET("/frontend/*", func(ec echo.Context) error {
path := strings.TrimPrefix(ec.Request().URL.Path, "/frontend/")
apiGroup.GET("/frontend/*", func(ec echo.Context) error {
path := strings.TrimPrefix(ec.Request().URL.Path, "/api/v1/frontend/")

if path == "" {
path = "index.html"
Expand Down
24 changes: 12 additions & 12 deletions http/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,19 @@ func TestHandlers(t *testing.T) {
payload string
}{
{
endpoint: "/healthcheck/dns",
endpoint: "/api/v1/healthcheck/dns",
payload: `{"name":"foo","description":"bar","domain":"mcorbin.fr","interval":"10m","one-off":false, "timeout": "1s"}`,
},
{
endpoint: "/healthcheck/tcp",
endpoint: "/api/v1/healthcheck/tcp",
payload: `{"name":"bar","description":"bar","interval":"10m","one-off":false,"target":"mcorbin.fr","port":9999,"timeout":"10s"}`,
},
{
endpoint: "/healthcheck/http",
endpoint: "/api/v1/healthcheck/http",
payload: `{"name":"baz","description":"bar","domain":"mcorbin.fr","interval":"10m","one-off":false,"target":"mcorbin.fr","port":9999,"timeout":"10s","protocol":"http","valid-status":[200]}`,
},
{
endpoint: "/healthcheck/tls",
endpoint: "/api/v1/healthcheck/tls",
payload: `{"name":"tls-check","description":"bar","interval":"10m","one-off":false,"target":"mcorbin.fr","port":9999,"timeout":"10s"}`,
},
}
Expand All @@ -78,7 +78,7 @@ func TestHandlers(t *testing.T) {
}

// get the healthchecks
resp, err := http.Get("http://127.0.0.1:2001/healthcheck")
resp, err := http.Get("http://127.0.0.1:2001/api/v1/healthcheck")
if err != nil {
t.Fatalf("Fail to get the healthchecks\n%v", err)
}
Expand All @@ -98,7 +98,7 @@ func TestHandlers(t *testing.T) {
t.Fatalf("Invalid body\n")
}
// get one healthcheck
resp, err = http.Get("http://127.0.0.1:2001/healthcheck/foo")
resp, err = http.Get("http://127.0.0.1:2001/api/v1/healthcheck/foo")
if err != nil {
t.Fatalf("Fail to get the healthchecks\n%v", err)
}
Expand All @@ -112,7 +112,7 @@ func TestHandlers(t *testing.T) {
t.Fatalf("Invalid body\n")
}
// get one invalid healthcheck
resp, err = http.Get("http://127.0.0.1:2001/healthcheck/doesnotexist")
resp, err = http.Get("http://127.0.0.1:2001/api/v1/healthcheck/doesnotexist")
if err != nil {
t.Fatalf("Fail to get the healthchecks\n%v", err)
}
Expand All @@ -131,7 +131,7 @@ func TestHandlers(t *testing.T) {
// delete everything
checks := []string{"foo", "bar", "baz", "tls-check"}
for _, c := range checks {
req, err := http.NewRequest("DELETE", fmt.Sprintf("http://127.0.0.1:2001/healthcheck/%s", c), nil)
req, err := http.NewRequest("DELETE", fmt.Sprintf("http://127.0.0.1:2001/api/v1/healthcheck/%s", c), nil)
if err != nil {
t.Fatalf("Fail to build the HTTP request\n%v", err)
}
Expand Down Expand Up @@ -196,7 +196,7 @@ func TestOneOffCheck(t *testing.T) {
}
client := &http.Client{}
reqBody := fmt.Sprintf(`{"name":"baz","description":"bar","interval":"10m","one-off":true,"target":"127.0.0.1","port":%d,"timeout":"10s","protocol":"http","valid-status":[200]}`, port)
req, err := http.NewRequest("POST", "http://127.0.0.1:2001/healthcheck/http", bytes.NewBuffer([]byte(reqBody)))
req, err := http.NewRequest("POST", "http://127.0.0.1:2001/api/v1/healthcheck/http", bytes.NewBuffer([]byte(reqBody)))
req.Header.Set("Content-Type", "application/json")
if err != nil {
t.Fatalf("Fail to build the HTTP request\n%v", err)
Expand Down Expand Up @@ -247,7 +247,7 @@ func TestBulkEndpoint(t *testing.T) {

client := &http.Client{}
reqBody := `{"http-checks": [{"name":"baz","description":"bar","interval":"10m","target":"127.0.0.1","port":3000,"timeout":"10s","protocol":"http","valid-status":[200],"body-regexp":["test*"]}]}`
req, err := http.NewRequest("POST", "http://127.0.0.1:2001/healthcheck/bulk", bytes.NewBuffer([]byte(reqBody)))
req, err := http.NewRequest("POST", "http://127.0.0.1:2001/api/v1/healthcheck/bulk", bytes.NewBuffer([]byte(reqBody)))
req.Header.Set("Content-Type", "application/json")
if err != nil {
t.Fatalf("Fail to build the HTTP request\n%v", err)
Expand Down Expand Up @@ -320,14 +320,14 @@ func TestBasicAuth(t *testing.T) {
if err != nil {
t.Fatalf("Fail to start the component\n%v", err)
}
resp, err := http.Get("http://127.0.0.1:2001/result")
resp, err := http.Get("http://127.0.0.1:2001/api/v1/result")
if err != nil {
t.Fatalf("HTTP request failed\n%v", err)
}
if resp.StatusCode != 401 {
t.Fatalf("Expected 401, got status %d", resp.StatusCode)
}
req, err := http.NewRequest("GET", "http://127.0.0.1:2001/result", nil)
req, err := http.NewRequest("GET", "http://127.0.0.1:2001/api/v1/result", nil)
if err != nil {
t.Fatalf("Fail to build the request\n%v", err)
}
Expand Down

0 comments on commit b225d3a

Please sign in to comment.