mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-12 06:24:10 +01:00
web: add a reverse proxy to Grafana
This commit is contained in:
@@ -31,6 +31,7 @@ type ServeConfiguration struct {
|
|||||||
GeoIP geoip.Configuration
|
GeoIP geoip.Configuration
|
||||||
Kafka kafka.Configuration
|
Kafka kafka.Configuration
|
||||||
Core core.Configuration
|
Core core.Configuration
|
||||||
|
Web web.Configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultServeConfiguration is the default configuration for the serve command.
|
// DefaultServeConfiguration is the default configuration for the serve command.
|
||||||
@@ -42,6 +43,7 @@ var DefaultServeConfiguration = ServeConfiguration{
|
|||||||
GeoIP: geoip.DefaultConfiguration,
|
GeoIP: geoip.DefaultConfiguration,
|
||||||
Kafka: kafka.DefaultConfiguration,
|
Kafka: kafka.DefaultConfiguration,
|
||||||
Core: core.DefaultConfiguration,
|
Core: core.DefaultConfiguration,
|
||||||
|
Web: web.DefaultConfiguration,
|
||||||
}
|
}
|
||||||
|
|
||||||
type serveOptions struct {
|
type serveOptions struct {
|
||||||
@@ -156,7 +158,7 @@ func daemonStart(r *reporter.Reporter, config ServeConfiguration, checkOnly bool
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to initialize core component: %w", err)
|
return fmt.Errorf("unable to initialize core component: %w", err)
|
||||||
}
|
}
|
||||||
webComponent, err := web.New(r, web.Dependencies{
|
webComponent, err := web.New(r, config.Web, web.Dependencies{
|
||||||
HTTP: httpComponent,
|
HTTP: httpComponent,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ configured through a different section:
|
|||||||
|
|
||||||
- `reporting`: [Log and metric reporting](#reporting)
|
- `reporting`: [Log and metric reporting](#reporting)
|
||||||
- `http`: [Builtin HTTP server](#http)
|
- `http`: [Builtin HTTP server](#http)
|
||||||
|
- `web`: [Web interface](#web)
|
||||||
- `flow`: [Flow ingestion](#flow)
|
- `flow`: [Flow ingestion](#flow)
|
||||||
- `snmp`: [SNMP poller](#snmp)
|
- `snmp`: [SNMP poller](#snmp)
|
||||||
- `geoip`: [GeoIP database](#geoip)
|
- `geoip`: [GeoIP database](#geoip)
|
||||||
@@ -34,6 +35,14 @@ http:
|
|||||||
listen: 0.0.0.0:8000
|
listen: 0.0.0.0:8000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Web
|
||||||
|
|
||||||
|
The web interface presents the landing page of *Akvorado*. It also
|
||||||
|
embeds the documentation. It accepts only the following key:
|
||||||
|
|
||||||
|
- `grafanaurl` to specify the URL to Grafana and exposes it as
|
||||||
|
`/grafana`.
|
||||||
|
|
||||||
## Flow
|
## Flow
|
||||||
|
|
||||||
The flow component handles flow ingestion. It supports the following
|
The flow component handles flow ingestion. It supports the following
|
||||||
|
|||||||
@@ -23,5 +23,6 @@ The embedded HTTP server serves the following endpoints:
|
|||||||
- [`/healthcheck`](/healthcheck){ target=http }: are we alive?
|
- [`/healthcheck`](/healthcheck){ target=http }: are we alive?
|
||||||
- [`/flows`](/flows?limit=1){ target=http }: next available flow
|
- [`/flows`](/flows?limit=1){ target=http }: next available flow
|
||||||
- [`/flow.proto`](/flow.proto){ target=http }: protocol buffers definition
|
- [`/flow.proto`](/flow.proto){ target=http }: protocol buffers definition
|
||||||
|
- [`/grafana`](/grafana): Grafana web interface (if configured)
|
||||||
|
|
||||||
<iframe name="http" style="width: 100%; height: 200px; border: 0; background-color: #1111"></iframe>
|
<iframe name="http" style="width: 100%; height: 200px; border: 0; background-color: #1111"></iframe>
|
||||||
|
|||||||
10
web/config.go
Normal file
10
web/config.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
// Configuration describes the configuration for the web component.
|
||||||
|
type Configuration struct {
|
||||||
|
// GrafanaURL is the URL to acess Grafana.
|
||||||
|
GrafanaURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfiguration represents the default configuration for the web exporter.
|
||||||
|
var DefaultConfiguration = Configuration{}
|
||||||
31
web/root.go
31
web/root.go
@@ -6,6 +6,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
netHTTP "net/http"
|
netHTTP "net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"akvorado/http"
|
"akvorado/http"
|
||||||
"akvorado/reporter"
|
"akvorado/reporter"
|
||||||
@@ -16,8 +18,9 @@ var rootSite embed.FS
|
|||||||
|
|
||||||
// Component represents the web component.
|
// Component represents the web component.
|
||||||
type Component struct {
|
type Component struct {
|
||||||
r *reporter.Reporter
|
r *reporter.Reporter
|
||||||
d *Dependencies
|
d *Dependencies
|
||||||
|
config Configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dependencies define the dependencies of the web component.
|
// Dependencies define the dependencies of the web component.
|
||||||
@@ -26,15 +29,33 @@ type Dependencies struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new web component.
|
// New creates a new web component.
|
||||||
func New(reporter *reporter.Reporter, dependencies Dependencies) (*Component, error) {
|
func New(reporter *reporter.Reporter, config Configuration, dependencies Dependencies) (*Component, error) {
|
||||||
c := Component{
|
c := Component{
|
||||||
r: reporter,
|
r: reporter,
|
||||||
d: &dependencies,
|
d: &dependencies,
|
||||||
|
config: config,
|
||||||
}
|
}
|
||||||
data, err := fs.Sub(rootSite, "data")
|
data, err := fs.Sub(rootSite, "data")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to get embedded website: %w", err)
|
return nil, fmt.Errorf("unable to get embedded website: %w", err)
|
||||||
}
|
}
|
||||||
c.d.HTTP.AddHandler("/", netHTTP.FileServer(netHTTP.FS(data)))
|
c.d.HTTP.AddHandler("/", netHTTP.FileServer(netHTTP.FS(data)))
|
||||||
|
if c.config.GrafanaURL != "" {
|
||||||
|
// Provide a proxy for Grafana
|
||||||
|
url, err := url.Parse(config.GrafanaURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse Grafana URL %q: %w", config.GrafanaURL, err)
|
||||||
|
}
|
||||||
|
proxy := httputil.NewSingleHostReverseProxy(url)
|
||||||
|
proxy.Transport = &netHTTP.Transport{
|
||||||
|
Proxy: nil, // Disable proxy
|
||||||
|
}
|
||||||
|
proxyHandler := netHTTP.HandlerFunc(
|
||||||
|
func(w netHTTP.ResponseWriter, r *netHTTP.Request) {
|
||||||
|
fmt.Println("hello")
|
||||||
|
proxy.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
c.d.HTTP.AddHandler("/grafana/", proxyHandler)
|
||||||
|
}
|
||||||
return &c, nil
|
return &c, nil
|
||||||
}
|
}
|
||||||
|
|||||||
49
web/root_test.go
Normal file
49
web/root_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
netHTTP "net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"akvorado/helpers"
|
||||||
|
"akvorado/http"
|
||||||
|
"akvorado/reporter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProxy(t *testing.T) {
|
||||||
|
// Mock HTTP server
|
||||||
|
server := httptest.NewServer(
|
||||||
|
netHTTP.HandlerFunc(
|
||||||
|
func(w netHTTP.ResponseWriter, r *netHTTP.Request) {
|
||||||
|
fmt.Fprintf(w, "hello world!")
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
r := reporter.NewMock(t)
|
||||||
|
h := http.NewMock(t, r)
|
||||||
|
_, err := New(r, Configuration{
|
||||||
|
GrafanaURL: server.URL,
|
||||||
|
}, Dependencies{HTTP: h})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New() error:\n%+v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the proxy works as expected
|
||||||
|
resp, err := netHTTP.Get(fmt.Sprintf("http://%s/grafana/test", h.Address))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GET /grafana/test:\n%+v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GET /grafana/test: cannot read body:\n%+v", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
t.Errorf("GET /grafana/test: got status code %d, not 200", resp.StatusCode)
|
||||||
|
}
|
||||||
|
if diff := helpers.Diff(string(body), "hello world!"); diff != "" {
|
||||||
|
t.Errorf("GET /grafana/test (-got, +want):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user