mirror of
https://github.com/containrrr/watchtower.git
synced 2026-01-12 20:18:50 +01:00
http report wip
This commit is contained in:
parent
e3dd8d688a
commit
efaf7190ee
25 changed files with 350 additions and 284 deletions
|
|
@ -2,6 +2,7 @@ package api
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/containrrr/watchtower/pkg/session"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
|
@ -10,8 +11,9 @@ const tokenMissingMsg = "api token is empty or has not been set. exiting"
|
|||
|
||||
// API is the http server responsible for serving the HTTP API endpoints
|
||||
type API struct {
|
||||
Token string
|
||||
hasHandlers bool
|
||||
Token string
|
||||
hasHandlers bool
|
||||
latestReport session.Report
|
||||
}
|
||||
|
||||
// New is a factory function creating a new API instance
|
||||
|
|
@ -74,3 +76,7 @@ func runHTTPServer() {
|
|||
log.Info("Serving HTTP")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
|
||||
func (api *API) UpdateReport(report session.Report) {
|
||||
api.latestReport = report
|
||||
}
|
||||
|
|
|
|||
13
pkg/api/api_suite_test.go
Normal file
13
pkg/api/api_suite_test.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package api_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestAPI(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "API Suite")
|
||||
}
|
||||
23
pkg/api/json.go
Normal file
23
pkg/api/json.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// WriteJsonOrError writes the supplied response to the http.ResponseWriter, handling any errors by logging and
|
||||
// returning an Internal Server Error response (status 500)
|
||||
func WriteJsonOrError(writer http.ResponseWriter, response interface{}) {
|
||||
data, err := json.MarshalIndent(response, "", " ")
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("failed to create json payload")
|
||||
writer.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
_, err = writer.Write(data)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("failed to write response")
|
||||
}
|
||||
}
|
||||
32
pkg/api/metrics.go
Normal file
32
pkg/api/metrics.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/containrrr/watchtower/pkg/metrics"
|
||||
"net/http"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// MetricsHandler is a HTTP handler for serving metric data
|
||||
type MetricsHandler struct {
|
||||
Path string
|
||||
Handle http.HandlerFunc
|
||||
Metrics *metrics.Metrics
|
||||
}
|
||||
|
||||
// NewMetricsHandler is a factory function creating a new Metrics instance
|
||||
func NewMetricsHandler() *MetricsHandler {
|
||||
m := metrics.Default()
|
||||
handler := promhttp.Handler()
|
||||
|
||||
return &MetricsHandler{
|
||||
Path: "/v1/metrics",
|
||||
Handle: handler.ServeHTTP,
|
||||
Metrics: m,
|
||||
}
|
||||
}
|
||||
|
||||
func MetricsEndpoint() (path string, handler http.HandlerFunc) {
|
||||
mh := NewMetricsHandler()
|
||||
return mh.Path, mh.Handle
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/containrrr/watchtower/pkg/metrics"
|
||||
"net/http"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// Handler is an HTTP handle for serving metric data
|
||||
type Handler struct {
|
||||
Path string
|
||||
Handle http.HandlerFunc
|
||||
Metrics *metrics.Metrics
|
||||
}
|
||||
|
||||
// New is a factory function creating a new Metrics instance
|
||||
func New() *Handler {
|
||||
m := metrics.Default()
|
||||
handler := promhttp.Handler()
|
||||
|
||||
return &Handler{
|
||||
Path: "/v1/metrics",
|
||||
Handle: handler.ServeHTTP,
|
||||
Metrics: m,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package metrics_test
|
||||
package api_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -6,10 +6,8 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containrrr/watchtower/pkg/api"
|
||||
metricsAPI "github.com/containrrr/watchtower/pkg/api/metrics"
|
||||
"github.com/containrrr/watchtower/pkg/metrics"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
|
@ -21,11 +19,6 @@ const (
|
|||
getURL = "http://localhost:8080/v1/metrics"
|
||||
)
|
||||
|
||||
func TestMetrics(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Metrics Suite")
|
||||
}
|
||||
|
||||
func getWithToken(handler http.Handler) map[string]string {
|
||||
metricMap := map[string]string{}
|
||||
respWriter := httptest.NewRecorder()
|
||||
|
|
@ -51,7 +44,7 @@ func getWithToken(handler http.Handler) map[string]string {
|
|||
|
||||
var _ = Describe("the metrics API", func() {
|
||||
httpAPI := api.New(token)
|
||||
m := metricsAPI.New()
|
||||
m := api.NewMetricsHandler()
|
||||
|
||||
handleReq := httpAPI.RequireToken(m.Handle)
|
||||
tryGetMetrics := func() map[string]string { return getWithToken(handleReq) }
|
||||
14
pkg/api/report.go
Normal file
14
pkg/api/report.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/containrrr/watchtower/pkg/session"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func ReportEndpoint(reportPtr **session.Report) (path string, handler http.HandlerFunc) {
|
||||
path = "/v1/report"
|
||||
handler = func(writer http.ResponseWriter, request *http.Request) {
|
||||
WriteJsonOrError(writer, *reportPtr)
|
||||
}
|
||||
return path, handler
|
||||
}
|
||||
62
pkg/api/update.go
Normal file
62
pkg/api/update.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/containrrr/watchtower/pkg/metrics"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
lock chan bool
|
||||
)
|
||||
|
||||
// NewUpdateHandler is a factory function creating a new Handler instance
|
||||
func NewUpdateHandler(updateFn func() *metrics.Metric, updateLock chan bool) *UpdateHandler {
|
||||
if updateLock != nil {
|
||||
lock = updateLock
|
||||
} else {
|
||||
lock = make(chan bool, 1)
|
||||
lock <- true
|
||||
}
|
||||
|
||||
return &UpdateHandler{
|
||||
fn: updateFn,
|
||||
Path: "/v1/update",
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateEndpoint(updateFn func() *metrics.Metric, updateLock chan bool) (path string, handler http.HandlerFunc) {
|
||||
uh := NewUpdateHandler(updateFn, updateLock)
|
||||
return uh.Path, uh.Handle
|
||||
}
|
||||
|
||||
// UpdateHandler is an API handler used for triggering container update scans
|
||||
type UpdateHandler struct {
|
||||
fn func() *metrics.Metric
|
||||
Path string
|
||||
}
|
||||
|
||||
// Handle is the actual http.Handle function doing all the heavy lifting
|
||||
func (handler *UpdateHandler) Handle(w http.ResponseWriter, _ *http.Request) {
|
||||
log.Info("Updates triggered by HTTP API request.")
|
||||
|
||||
result := updateResult{}
|
||||
|
||||
select {
|
||||
case chanValue := <-lock:
|
||||
defer func() { lock <- chanValue }()
|
||||
metric := handler.fn()
|
||||
metrics.RegisterScan(metric)
|
||||
result.Result = metric
|
||||
result.Skipped = false
|
||||
default:
|
||||
log.Debug("Skipped. Another update already running.")
|
||||
result.Skipped = true
|
||||
}
|
||||
WriteJsonOrError(w, result)
|
||||
}
|
||||
|
||||
type updateResult struct {
|
||||
Skipped bool
|
||||
Result *metrics.Metric
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
package update
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
lock chan bool
|
||||
)
|
||||
|
||||
// New is a factory function creating a new Handler instance
|
||||
func New(updateFn func(), updateLock chan bool) *Handler {
|
||||
if updateLock != nil {
|
||||
lock = updateLock
|
||||
} else {
|
||||
lock = make(chan bool, 1)
|
||||
lock <- true
|
||||
}
|
||||
|
||||
return &Handler{
|
||||
fn: updateFn,
|
||||
Path: "/v1/update",
|
||||
}
|
||||
}
|
||||
|
||||
// Handler is an API handler used for triggering container update scans
|
||||
type Handler struct {
|
||||
fn func()
|
||||
Path string
|
||||
}
|
||||
|
||||
// Handle is the actual http.Handle function doing all the heavy lifting
|
||||
func (handle *Handler) Handle(w http.ResponseWriter, r *http.Request) {
|
||||
log.Info("Updates triggered by HTTP API request.")
|
||||
|
||||
_, err := io.Copy(os.Stdout, r.Body)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case chanValue := <-lock:
|
||||
defer func() { lock <- chanValue }()
|
||||
handle.fn()
|
||||
default:
|
||||
log.Debug("Skipped. Another update already running.")
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue