Initial integration tests for config settings
parent
6d93507389
commit
f3f1cc1064
|
@ -44,6 +44,9 @@ type Command struct {
|
|||
Logger zap.Logger
|
||||
|
||||
Server *Server
|
||||
|
||||
// How to get environment variables. Normally set to os.Getenv, except for tests.
|
||||
Getenv func(string) string
|
||||
}
|
||||
|
||||
// NewCommand return a new instance of Command.
|
||||
|
@ -67,7 +70,7 @@ func (cmd *Command) Run(args ...string) error {
|
|||
}
|
||||
|
||||
// Print sweet InfluxDB logo.
|
||||
fmt.Print(logo)
|
||||
fmt.Fprint(cmd.Stdout, logo)
|
||||
|
||||
// Mark start-up in log.
|
||||
cmd.Logger.Info(fmt.Sprintf("InfluxDB starting, version %s, branch %s, commit %s",
|
||||
|
@ -86,7 +89,7 @@ func (cmd *Command) Run(args ...string) error {
|
|||
}
|
||||
|
||||
// Apply any environment variables on top of the parsed config
|
||||
if err := config.ApplyEnvOverrides(); err != nil {
|
||||
if err := config.ApplyEnvOverrides(cmd.Getenv); err != nil {
|
||||
return fmt.Errorf("apply env config: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -185,18 +185,21 @@ func (c *Config) Validate() error {
|
|||
}
|
||||
|
||||
// ApplyEnvOverrides apply the environment configuration on top of the config.
|
||||
func (c *Config) ApplyEnvOverrides() error {
|
||||
return c.applyEnvOverrides("INFLUXDB", reflect.ValueOf(c), "")
|
||||
func (c *Config) ApplyEnvOverrides(getenv func(string) string) error {
|
||||
if getenv == nil {
|
||||
getenv = os.Getenv
|
||||
}
|
||||
return c.applyEnvOverrides(getenv, "INFLUXDB", reflect.ValueOf(c), "")
|
||||
}
|
||||
|
||||
func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value, structKey string) error {
|
||||
func (c *Config) applyEnvOverrides(getenv func(string) string, prefix string, spec reflect.Value, structKey string) error {
|
||||
// If we have a pointer, dereference it
|
||||
element := spec
|
||||
if spec.Kind() == reflect.Ptr {
|
||||
element = spec.Elem()
|
||||
}
|
||||
|
||||
value := os.Getenv(prefix)
|
||||
value := getenv(prefix)
|
||||
|
||||
switch element.Kind() {
|
||||
case reflect.String:
|
||||
|
@ -244,11 +247,11 @@ func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value, structKey
|
|||
// If the type is s slice, apply to each using the index as a suffix, e.g. GRAPHITE_0, GRAPHITE_0_TEMPLATES_0 or GRAPHITE_0_TEMPLATES="item1,item2"
|
||||
for j := 0; j < element.Len(); j++ {
|
||||
f := element.Index(j)
|
||||
if err := c.applyEnvOverrides(prefix, f, structKey); err != nil {
|
||||
if err := c.applyEnvOverrides(getenv, prefix, f, structKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.applyEnvOverrides(fmt.Sprintf("%s_%d", prefix, j), f, structKey); err != nil {
|
||||
if err := c.applyEnvOverrides(getenv, fmt.Sprintf("%s_%d", prefix, j), f, structKey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -285,19 +288,19 @@ func (c *Config) applyEnvOverrides(prefix string, spec reflect.Value, structKey
|
|||
// If it's a sub-config, recursively apply
|
||||
if field.Kind() == reflect.Struct || field.Kind() == reflect.Ptr ||
|
||||
field.Kind() == reflect.Slice || field.Kind() == reflect.Array {
|
||||
if err := c.applyEnvOverrides(envKey, field, fieldName); err != nil {
|
||||
if err := c.applyEnvOverrides(getenv, envKey, field, fieldName); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
value := os.Getenv(envKey)
|
||||
value := getenv(envKey)
|
||||
// Skip any fields we don't have a value to set
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := c.applyEnvOverrides(envKey, field, fieldName); err != nil {
|
||||
if err := c.applyEnvOverrides(getenv, envKey, field, fieldName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ func (cmd *PrintConfigCommand) Run(args ...string) error {
|
|||
}
|
||||
|
||||
// Apply any environment variables on top of the parsed config
|
||||
if err := config.ApplyEnvOverrides(); err != nil {
|
||||
if err := config.ApplyEnvOverrides(os.Getenv); err != nil {
|
||||
return fmt.Errorf("apply env config: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -180,7 +180,7 @@ enabled = true
|
|||
t.Fatalf("failed to set env var: %v", err)
|
||||
}
|
||||
|
||||
if err := c.ApplyEnvOverrides(); err != nil {
|
||||
if err := c.ApplyEnvOverrides(os.Getenv); err != nil {
|
||||
t.Fatalf("failed to apply env overrides: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package cmd_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
client "github.com/influxdata/influxdb/client/v2"
|
||||
)
|
||||
|
||||
func TestRetentionAutocreate(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
enabled bool
|
||||
}{
|
||||
{name: "enabled", enabled: true},
|
||||
{name: "disabled", enabled: false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := NewTestRunCommand(map[string]string{
|
||||
"INFLUXDB_META_RETENTION_AUTOCREATE": strconv.FormatBool(tc.enabled),
|
||||
})
|
||||
defer cmd.Cleanup()
|
||||
|
||||
cmd.MustRun()
|
||||
|
||||
c, err := client.NewHTTPClient(client.HTTPConfig{
|
||||
Addr: "http://" + cmd.HTTPAddr(),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_ = mustQuery(c, "CREATE DATABASE test", "", "")
|
||||
|
||||
resp := mustQuery(c, "SHOW RETENTION POLICIES ON test", "", "")
|
||||
if len(resp.Results) != 1 {
|
||||
t.Fatalf("expected 1 result in response, got %d", len(resp.Results))
|
||||
}
|
||||
|
||||
if tc.enabled {
|
||||
if len(resp.Results[0].Series) != 1 || len(resp.Results[0].Series[0].Values) != 1 {
|
||||
t.Fatalf("expected one automatically created retention policy, got %#v", resp.Results[0].Series[0].Values)
|
||||
}
|
||||
} else {
|
||||
if len(resp.Results[0].Series) != 1 || len(resp.Results[0].Series[0].Values) != 0 {
|
||||
t.Fatalf("expected no retention policies, got: %#v", resp.Results[0].Series[0].Values)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheMaxMemorySize(t *testing.T) {
|
||||
cmd := NewTestRunCommand(map[string]string{
|
||||
"INFLUXDB_DATA_CACHE_MAX_MEMORY_SIZE": "1024",
|
||||
})
|
||||
defer cmd.Cleanup()
|
||||
|
||||
cmd.MustRun()
|
||||
|
||||
c := cmd.HTTPClient()
|
||||
_ = mustQuery(c, "CREATE DATABASE test", "", "")
|
||||
|
||||
// Add a small point that fits in the cache size.
|
||||
bp, _ := client.NewBatchPoints(client.BatchPointsConfig{Database: "test"})
|
||||
pt, _ := client.NewPoint("strings", nil, map[string]interface{}{"s": "a short string"})
|
||||
bp.AddPoint(pt)
|
||||
if err := c.Write(bp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// This point won't fit in the cache size and should be rejected.
|
||||
bp, _ = client.NewBatchPoints(client.BatchPointsConfig{Database: "test"})
|
||||
pt, _ = client.NewPoint("strings", nil, map[string]interface{}{"s": strings.Repeat("long", 1024)})
|
||||
bp.AddPoint(pt)
|
||||
err := c.Write(bp)
|
||||
if err == nil {
|
||||
t.Fatal("expected an error but got nil")
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "cache-max-memory-size") {
|
||||
t.Fatalf("unexpected error: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func mustQuery(c client.Client, q, db, precision string) *client.Response {
|
||||
resp, err := c.Query(client.NewQuery(q, db, precision))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else if resp.Error() != nil {
|
||||
panic(resp.Error())
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package cmd_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
client "github.com/influxdata/influxdb/client/v2"
|
||||
"github.com/influxdata/influxdb/cmd/influxd/run"
|
||||
"github.com/influxdata/influxdb/services/httpd"
|
||||
)
|
||||
|
||||
type TestRunCommand struct {
|
||||
*run.Command
|
||||
|
||||
// Temporary directory used for default data, meta, and wal dirs.
|
||||
Dir string
|
||||
}
|
||||
|
||||
func NewTestRunCommand(env map[string]string) *TestRunCommand {
|
||||
dir, err := ioutil.TempDir("", "testrun-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cmd := run.NewCommand()
|
||||
cmd.Getenv = func(k string) string {
|
||||
// Return value in env map, if set.
|
||||
var v string
|
||||
if env != nil {
|
||||
v = env[k]
|
||||
}
|
||||
if v != "" {
|
||||
return v
|
||||
}
|
||||
|
||||
// If the key wasn't explicitly set in env, use some reasonable defaults for test.
|
||||
switch k {
|
||||
case "INFLUXDB_DATA_DIR":
|
||||
return filepath.Join(dir, "data")
|
||||
case "INFLUXDB_META_DIR":
|
||||
return filepath.Join(dir, "meta")
|
||||
case "INFLUXDB_WAL_DIR":
|
||||
return filepath.Join(dir, "wal")
|
||||
case "INFLUXDB_HTTP_BIND_ADDRESS":
|
||||
return "localhost:0"
|
||||
case "INFLUXDB_BIND_ADDRESS":
|
||||
return "localhost:0"
|
||||
case "INFLUXDB_REPORTING_DISABLED":
|
||||
return "true"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return &TestRunCommand{
|
||||
Command: cmd,
|
||||
Dir: dir,
|
||||
}
|
||||
}
|
||||
|
||||
// MustRun calls Command.Run and panics if there is an error.
|
||||
func (c *TestRunCommand) MustRun() {
|
||||
if err := c.Command.Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPClient returns a new v2 HTTP client.
|
||||
func (c *TestRunCommand) HTTPClient() client.Client {
|
||||
cl, err := client.NewHTTPClient(client.HTTPConfig{
|
||||
Addr: "http://" + c.HTTPAddr(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cl
|
||||
}
|
||||
|
||||
// HTTPAddr returns the bind address of the HTTP service, in form "localhost:65432".
|
||||
func (c *TestRunCommand) HTTPAddr() string {
|
||||
for _, s := range c.Command.Server.Services {
|
||||
if s, ok := s.(*httpd.Service); ok {
|
||||
return s.HTTPAddr()
|
||||
}
|
||||
}
|
||||
panic("Did not find HTTPD service!")
|
||||
}
|
||||
|
||||
func (c *TestRunCommand) Cleanup() {
|
||||
c.Command.Close()
|
||||
os.RemoveAll(c.Dir)
|
||||
}
|
|
@ -198,6 +198,12 @@ func (s *Service) Statistics(tags map[string]string) []models.Statistic {
|
|||
return s.Handler.Statistics(models.NewTags(map[string]string{"bind": s.addr}).Merge(tags).Map())
|
||||
}
|
||||
|
||||
// HTTPAddr returns the string version of the address that the HTTP server is listening on.
|
||||
// This is useful if you start an ephemeral server in test with bind address localhost:0.
|
||||
func (s *Service) HTTPAddr() string {
|
||||
return s.ln.Addr().String()
|
||||
}
|
||||
|
||||
// serveTCP serves the handler from the TCP listener.
|
||||
func (s *Service) serveTCP() {
|
||||
s.serve(s.ln)
|
||||
|
|
Loading…
Reference in New Issue