mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
cmd: accept URL as a configuration file
This commit is contained in:
@@ -1,9 +1,12 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -27,12 +30,37 @@ type ConfigRelatedOptions struct {
|
|||||||
func (c ConfigRelatedOptions) Parse(out io.Writer, component string, config interface{}) error {
|
func (c ConfigRelatedOptions) Parse(out io.Writer, component string, config interface{}) error {
|
||||||
var rawConfig map[string]interface{}
|
var rawConfig map[string]interface{}
|
||||||
if cfgFile := c.Path; cfgFile != "" {
|
if cfgFile := c.Path; cfgFile != "" {
|
||||||
input, err := ioutil.ReadFile(cfgFile)
|
if strings.HasPrefix(cfgFile, "http://") || strings.HasPrefix(cfgFile, "https://") {
|
||||||
if err != nil {
|
u, err := url.Parse(cfgFile)
|
||||||
return fmt.Errorf("unable to read configuration file: %w", err)
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("cannot parse configuration URL: %w", err)
|
||||||
if err := yaml.Unmarshal(input, &rawConfig); err != nil {
|
}
|
||||||
return fmt.Errorf("unable to parse configuration file: %w", err)
|
if u.Path == "" {
|
||||||
|
u.Path = fmt.Sprintf("/api/v0/orchestrator/configuration/%s", component)
|
||||||
|
}
|
||||||
|
resp, err := http.Get(u.String())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to fetch configuration file: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if contentType := resp.Header.Get("Content-Type"); contentType != "application/json" {
|
||||||
|
return fmt.Errorf("received configuration file is not JSON (%s)", contentType)
|
||||||
|
}
|
||||||
|
input, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read configuration file: %w", err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(input, &rawConfig); err != nil {
|
||||||
|
return fmt.Errorf("unable to parse JSON configuration file: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input, err := ioutil.ReadFile(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read configuration file: %w", err)
|
||||||
|
}
|
||||||
|
if err := yaml.Unmarshal(input, &rawConfig); err != nil {
|
||||||
|
return fmt.Errorf("unable to parse configuration file: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ package cmd_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -40,21 +44,23 @@ type dummyModule2DetailsConfiguration struct {
|
|||||||
IntervalValue time.Duration
|
IntervalValue time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
var dummyDefaultConfiguration = dummyConfiguration{
|
func dummyDefaultConfiguration() dummyConfiguration {
|
||||||
Module1: dummyModule1Configuration{
|
return dummyConfiguration{
|
||||||
Listen: "127.0.0.1:8080",
|
Module1: dummyModule1Configuration{
|
||||||
Topic: "nothingness",
|
Listen: "127.0.0.1:8080",
|
||||||
Workers: 100,
|
Topic: "nothingness",
|
||||||
},
|
Workers: 100,
|
||||||
Module2: dummyModule2Configuration{
|
|
||||||
MoreDetails: MoreDetails{
|
|
||||||
Stuff: "hello",
|
|
||||||
},
|
},
|
||||||
Details: dummyModule2DetailsConfiguration{
|
Module2: dummyModule2Configuration{
|
||||||
Workers: 1,
|
MoreDetails: MoreDetails{
|
||||||
IntervalValue: time.Minute,
|
Stuff: "hello",
|
||||||
|
},
|
||||||
|
Details: dummyModule2DetailsConfiguration{
|
||||||
|
Workers: 1,
|
||||||
|
IntervalValue: time.Minute,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDump(t *testing.T) {
|
func TestDump(t *testing.T) {
|
||||||
@@ -80,7 +86,7 @@ module2:
|
|||||||
Dump: true,
|
Dump: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed := dummyDefaultConfiguration
|
parsed := dummyDefaultConfiguration()
|
||||||
out := bytes.NewBuffer([]byte{})
|
out := bytes.NewBuffer([]byte{})
|
||||||
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
||||||
t.Fatalf("Parse() error:\n%+v", err)
|
t.Fatalf("Parse() error:\n%+v", err)
|
||||||
@@ -157,6 +163,15 @@ module2:
|
|||||||
ioutil.WriteFile(configFile, []byte(config), 0644)
|
ioutil.WriteFile(configFile, []byte(config), 0644)
|
||||||
|
|
||||||
// Environment
|
// Environment
|
||||||
|
clean := func() {
|
||||||
|
for _, env := range os.Environ() {
|
||||||
|
if strings.HasPrefix(env, "AKVORADO_DUMMY_") {
|
||||||
|
os.Unsetenv(strings.Split(env, "=")[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clean()
|
||||||
|
defer clean()
|
||||||
os.Setenv("AKVORADO_DUMMY_MODULE1_LISTEN", "127.0.0.1:9000")
|
os.Setenv("AKVORADO_DUMMY_MODULE1_LISTEN", "127.0.0.1:9000")
|
||||||
os.Setenv("AKVORADO_DUMMY_MODULE1_TOPIC", "something")
|
os.Setenv("AKVORADO_DUMMY_MODULE1_TOPIC", "something")
|
||||||
os.Setenv("AKVORADO_DUMMY_MODULE2_DETAILS_INTERVALVALUE", "10m")
|
os.Setenv("AKVORADO_DUMMY_MODULE2_DETAILS_INTERVALVALUE", "10m")
|
||||||
@@ -171,7 +186,7 @@ module2:
|
|||||||
Dump: true,
|
Dump: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed := dummyDefaultConfiguration
|
parsed := dummyDefaultConfiguration()
|
||||||
out := bytes.NewBuffer([]byte{})
|
out := bytes.NewBuffer([]byte{})
|
||||||
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
||||||
t.Fatalf("Parse() error:\n%+v", err)
|
t.Fatalf("Parse() error:\n%+v", err)
|
||||||
@@ -201,3 +216,63 @@ module2:
|
|||||||
t.Errorf("Parse() (-got, +want):\n%s", diff)
|
t.Errorf("Parse() (-got, +want):\n%s", diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPConfiguration(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
fmt.Fprint(w, `
|
||||||
|
{
|
||||||
|
"module1": {
|
||||||
|
"topic": "flows"
|
||||||
|
},
|
||||||
|
"module2": {
|
||||||
|
"details": {
|
||||||
|
"workers": 5,
|
||||||
|
"interval-value": "20m"
|
||||||
|
},
|
||||||
|
"stuff": "bye",
|
||||||
|
"elements": [
|
||||||
|
{"name": "first", "gauge": 67},
|
||||||
|
{"name": "second"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
c := cmd.ConfigRelatedOptions{
|
||||||
|
Path: ts.URL,
|
||||||
|
Dump: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed := dummyDefaultConfiguration()
|
||||||
|
out := bytes.NewBuffer([]byte{})
|
||||||
|
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
||||||
|
t.Fatalf("Parse() error:\n%+v", err)
|
||||||
|
}
|
||||||
|
// Expected configuration
|
||||||
|
expected := dummyConfiguration{
|
||||||
|
Module1: dummyModule1Configuration{
|
||||||
|
Listen: "127.0.0.1:8080",
|
||||||
|
Topic: "flows",
|
||||||
|
Workers: 100,
|
||||||
|
},
|
||||||
|
Module2: dummyModule2Configuration{
|
||||||
|
MoreDetails: MoreDetails{
|
||||||
|
Stuff: "bye",
|
||||||
|
},
|
||||||
|
Details: dummyModule2DetailsConfiguration{
|
||||||
|
Workers: 5,
|
||||||
|
IntervalValue: 20 * time.Minute,
|
||||||
|
},
|
||||||
|
Elements: []dummyModule2ElementsConfiguration{
|
||||||
|
{"first", 67},
|
||||||
|
{"second", 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if diff := helpers.Diff(parsed, expected); diff != "" {
|
||||||
|
t.Errorf("Parse() (-got, +want):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user