Initial integration tests for config settings

pull/8954/head
Mark Rushakoff 2017-10-11 17:16:42 -07:00
parent 6d93507389
commit f3f1cc1064
7 changed files with 214 additions and 13 deletions

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

93
cmd/integration_test.go Normal file
View File

@ -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)
}

View File

@ -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)