Skip to content
This repository was archived by the owner on Oct 11, 2023. It is now read-only.

Commit 7ee5d59

Browse files
author
Lucas SANTONI
committed
Create db through relay
1 parent 3e0b9e4 commit 7ee5d59

File tree

7 files changed

+150
-4
lines changed

7 files changed

+150
-4
lines changed

README.md

+38
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ ssl-combined-pem = "/path/to/influxdb-relay.pem"
112112
output = [
113113
# name: name of the backend, used for display purposes only.
114114
# location: full URL of the /write endpoint of the backend
115+
# admin: full URL of the /query endpoint of the backend. OPTIONAL.
115116
# timeout: Go-parseable time duration. Fail writes if incomplete in this time.
116117
# skip-tls-verification: skip verification for HTTPS location. WARNING: it's insecure. Don't use in production.
117118
# type: type of input source. OPTIONAL: see below for more information.
@@ -155,6 +156,43 @@ InfluxDB Relay is able to forward from a variety of input sources, including:
155156

156157
The `type` parameter in the configuration file defaults to `influxdb`.
157158

159+
### Administrative tasks
160+
161+
Whereas data manipulation relies on the `/write` endpoint, some other features
162+
such as database or user management are based on the `/query` endpoint. As
163+
InfluxDB Relay does not send back a response body to the client(s), we are not
164+
able to forward all of the features this endpoint provides. Still, we decided
165+
to expose it through the `/admin` route.
166+
167+
In order to use it, one shall define the optional `admin` parameter of the
168+
target backend(s) in the configuration file, as follows:
169+
170+
```toml
171+
[[http]]
172+
# Name of the HTTP server, used for display purposes only.
173+
name = "expose-database-creation"
174+
175+
# TCP address to bind to, for HTTP server.
176+
bind-addr = "127.0.0.1:9096"
177+
178+
# Array of InfluxDB instances to use as backends for Relay.
179+
output = [
180+
# InfluxDB
181+
{ name="local-influxdb01", location="http://127.0.0.1:8086/write", admin="http://127.0.0.1:8086/query", type="influxdb" },
182+
{ name="local-influxdb02", location="http://127.0.0.1:7086/write", admin="http://127.0.0.1:7086/query", type="influxdb" },
183+
]
184+
```
185+
186+
It is now possible to query the `/admin` endpoint. Its usage is the same as the
187+
standard `/query` Influx DB enpoint:
188+
189+
```
190+
curl -X POST "http://127.0.0.1:9096/admin" --data-urlencode 'q=CREATE DATABASE some_database'
191+
```
192+
193+
Errors will be logged just like regular `/write` queries. The HTTP response
194+
bodies will not be forwarded back to the clients.
195+
158196
## Limitations
159197

160198
So far, this is compatible with Debian, RedHat, and other derivatives.

config/config.go

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package config
22

33
import (
4+
"errors"
5+
"net/url"
46
"os"
57

68
"github.com/naoina/toml"
@@ -43,6 +45,10 @@ type HTTPOutputConfig struct {
4345
// Location should be set to the URL of the backend server's write endpoint
4446
Location string `toml:"location"`
4547

48+
// Query should be set to the URL of the backend server's query endpoint
49+
// This is used for database creation through relay
50+
Admin string `toml:"admin"`
51+
4652
// Timeout sets a per-backend timeout for write requests (default: 10s)
4753
// The format used is the same seen in time.ParseDuration
4854
Timeout string `toml:"timeout"`
@@ -113,6 +119,11 @@ func LoadConfigFile(filename string) (Config, error) {
113119
if backend.InputType == "" {
114120
cfg.HTTPRelays[index].Outputs[indexB].InputType = TypeInfluxdb
115121
}
122+
123+
_, err := url.Parse(backend.Admin)
124+
if err != nil {
125+
return cfg, errors.New("invalid query parameter for backend " + backend.Name)
126+
}
116127
}
117128
}
118129
}

dev/prepare_databases.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#! /usr/bin/env bash
22

3-
curl -X POST "http://127.0.0.1:8086/query" --data-urlencode 'q=CREATE DATABASE NOT_prometheus'
4-
curl -X POST "http://127.0.0.1:8086/query" --data-urlencode 'q=CREATE DATABASE prometheus'
3+
curl -v -X POST "http://127.0.0.1:9096/admin" --data-urlencode 'q=CREATE DATABASE NOT_prometheus'
4+
curl -v -X POST "http://127.0.0.1:9096/admin" --data-urlencode 'q=CREATE DATABASE prometheus'
55

66
# curl -X POST "http://127.0.0.1:8086/query" --data-urlencode 'db=prometheus' --data-urlencode 'q=SHOW SERIES'
77
# curl -X POST "http://127.0.0.1:8086/query" --data-urlencode 'db=NOT_prometheus' --data-urlencode 'q=SHOW SERIES'

dev/prometheus.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ remote_write:
33

44
scrape_configs:
55
- job_name: 'AUTOPARSE'
6-
scrape_interval: 1ms
6+
scrape_interval: 10s
77
static_configs:
88
- targets: ['localhost:9090']
99
labels:

dev/test.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
name = "test-config"
33
bind-addr = "0.0.0.0:9096"
44
output = [
5-
{ name="from_influx", location = "http://influxdb:8086/write" },
5+
{ name="from_influx", location = "http://influxdb:8086/write", admin = "http://influxdb:8086/query" },
66
{ name="from_prometheus", location = "http://influxdb:8086/api/v1/prom/write", type="prometheus" }
77
]

relay/http.go

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ var (
6060
"/api/v1/prom/write": (*HTTP).handleProm,
6161
"/ping": (*HTTP).handlePing,
6262
"/status": (*HTTP).handleStatus,
63+
"/admin": (*HTTP).handleAdmin,
6364
}
6465

6566
middlewares = []relayMiddleware{
@@ -278,6 +279,7 @@ type httpBackend struct {
278279
poster
279280
name string
280281
inputType config.Input
282+
admin string
281283
}
282284

283285
func newHTTPBackend(cfg *config.HTTPOutputConfig) (*httpBackend, error) {
@@ -321,6 +323,7 @@ func newHTTPBackend(cfg *config.HTTPOutputConfig) (*httpBackend, error) {
321323
poster: p,
322324
name: cfg.Name,
323325
inputType: cfg.InputType,
326+
admin: cfg.Admin,
324327
}, nil
325328
}
326329

relay/http_handlers.go

+94
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,100 @@ func (h *HTTP) handlePing(w http.ResponseWriter, r *http.Request) {
4040
}
4141
}
4242

43+
func (h *HTTP) handleAdmin(w http.ResponseWriter, r *http.Request) {
44+
// Client to perform the raw queries
45+
client := http.Client{}
46+
47+
if r.Method == http.MethodPost {
48+
// Responses
49+
var responses = make(chan *http.Response, len(h.backends))
50+
51+
// Associated waitgroup
52+
var wg sync.WaitGroup
53+
wg.Add(len(h.backends))
54+
55+
// Iterate over all backends
56+
for _, b := range h.backends {
57+
b := b
58+
59+
if b.admin == "" {
60+
// Empty query, skip backend
61+
wg.Done()
62+
continue
63+
}
64+
65+
go func() {
66+
defer wg.Done()
67+
68+
// Create new request
69+
// Update location according to backend
70+
// Forward body
71+
req, err := http.NewRequest("POST", b.admin, r.Body)
72+
if err != nil {
73+
jsonResponse(w, response{http.StatusServiceUnavailable, "could not prepare request: " + err.Error()})
74+
}
75+
76+
// Forward headers
77+
req.Header = r.Header
78+
79+
// Forward the request
80+
resp, err := client.Do(req)
81+
if err != nil {
82+
// Internal error
83+
log.Printf("Problem posting to relay %q backend %q: %v", h.Name(), b.name, err)
84+
85+
// So empty response
86+
responses <- &http.Response{}
87+
} else {
88+
if resp.StatusCode / 100 == 5 {
89+
// HTTP error
90+
log.Printf("5xx response for relay %q backend %q: %v", h.Name(), b.name, resp.StatusCode)
91+
}
92+
93+
// Get response
94+
responses <- resp
95+
}
96+
}()
97+
}
98+
99+
// Wait for requests
100+
go func() {
101+
wg.Wait()
102+
close(responses)
103+
}()
104+
105+
var errResponse *responseData
106+
for resp := range responses {
107+
switch resp.StatusCode / 100 {
108+
case 2:
109+
w.WriteHeader(http.StatusNoContent)
110+
return
111+
112+
case 4:
113+
// User error
114+
resp.Write(w)
115+
return
116+
117+
default:
118+
// Hold on to one of the responses to return back to the client
119+
errResponse = nil
120+
}
121+
}
122+
123+
// No successful writes
124+
if errResponse == nil {
125+
// Failed to make any valid request...
126+
jsonResponse(w, response{http.StatusServiceUnavailable, "unable to forward query"})
127+
return
128+
}
129+
130+
errResponse.Write(w)
131+
} else { // Bad method
132+
jsonResponse(w, response{http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed)})
133+
return
134+
}
135+
}
136+
43137
func (h *HTTP) handleStandard(w http.ResponseWriter, r *http.Request) {
44138
if r.Method != http.MethodPost {
45139
w.Header().Set("Allow", http.MethodPost)

0 commit comments

Comments
 (0)