mirror of
https://github.com/Oxalide/vsphere-influxdb-go.git
synced 2023-10-10 13:36:51 +02:00
296 lines
7.4 KiB
Go
296 lines
7.4 KiB
Go
|
package opentsdb
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"sync/atomic"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/davecgh/go-spew/spew"
|
||
|
"github.com/influxdata/influxdb/internal"
|
||
|
"github.com/influxdata/influxdb/models"
|
||
|
"github.com/influxdata/influxdb/services/meta"
|
||
|
"github.com/uber-go/zap"
|
||
|
)
|
||
|
|
||
|
func Test_Service_OpenClose(t *testing.T) {
|
||
|
// Let the OS assign a random port since we are only opening and closing the service,
|
||
|
// not actually connecting to it.
|
||
|
service := NewTestService("db0", "127.0.0.1:0")
|
||
|
|
||
|
// Closing a closed service is fine.
|
||
|
if err := service.Service.Close(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Closing a closed service again is fine.
|
||
|
if err := service.Service.Close(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if err := service.Service.Open(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Opening an already open service is fine.
|
||
|
if err := service.Service.Open(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Reopening a previously opened service is fine.
|
||
|
if err := service.Service.Close(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if err := service.Service.Open(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Tidy up.
|
||
|
if err := service.Service.Close(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Ensure a point can be written via the telnet protocol.
|
||
|
func TestService_CreatesDatabase(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
database := "db0"
|
||
|
s := NewTestService(database, "127.0.0.1:0")
|
||
|
s.WritePointsFn = func(string, string, models.ConsistencyLevel, []models.Point) error {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
called := make(chan struct{})
|
||
|
s.MetaClient.CreateDatabaseFn = func(name string) (*meta.DatabaseInfo, error) {
|
||
|
if name != database {
|
||
|
t.Errorf("\n\texp = %s\n\tgot = %s\n", database, name)
|
||
|
}
|
||
|
// Allow some time for the caller to return and the ready status to
|
||
|
// be set.
|
||
|
time.AfterFunc(10*time.Millisecond, func() { called <- struct{}{} })
|
||
|
return nil, errors.New("an error")
|
||
|
}
|
||
|
|
||
|
if err := s.Service.Open(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
points, err := models.ParsePointsString(`cpu value=1`)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
s.Service.batcher.In() <- points[0] // Send a point.
|
||
|
s.Service.batcher.Flush()
|
||
|
select {
|
||
|
case <-called:
|
||
|
// OK
|
||
|
case <-time.NewTimer(5 * time.Second).C:
|
||
|
t.Fatal("Service should have attempted to create database")
|
||
|
}
|
||
|
|
||
|
// ready status should not have been switched due to meta client error.
|
||
|
s.Service.mu.RLock()
|
||
|
ready := s.Service.ready
|
||
|
s.Service.mu.RUnlock()
|
||
|
|
||
|
if got, exp := ready, false; got != exp {
|
||
|
t.Fatalf("got %v, expected %v", got, exp)
|
||
|
}
|
||
|
|
||
|
// This time MC won't cause an error.
|
||
|
s.MetaClient.CreateDatabaseFn = func(name string) (*meta.DatabaseInfo, error) {
|
||
|
// Allow some time for the caller to return and the ready status to
|
||
|
// be set.
|
||
|
time.AfterFunc(10*time.Millisecond, func() { called <- struct{}{} })
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
s.Service.batcher.In() <- points[0] // Send a point.
|
||
|
s.Service.batcher.Flush()
|
||
|
select {
|
||
|
case <-called:
|
||
|
// OK
|
||
|
case <-time.NewTimer(5 * time.Second).C:
|
||
|
t.Fatal("Service should have attempted to create database")
|
||
|
}
|
||
|
|
||
|
// ready status should not have been switched due to meta client error.
|
||
|
s.Service.mu.RLock()
|
||
|
ready = s.Service.ready
|
||
|
s.Service.mu.RUnlock()
|
||
|
|
||
|
if got, exp := ready, true; got != exp {
|
||
|
t.Fatalf("got %v, expected %v", got, exp)
|
||
|
}
|
||
|
|
||
|
s.Service.Close()
|
||
|
}
|
||
|
|
||
|
// Ensure a point can be written via the telnet protocol.
|
||
|
func TestService_Telnet(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
s := NewTestService("db0", "127.0.0.1:0")
|
||
|
if err := s.Service.Open(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
defer s.Service.Close()
|
||
|
|
||
|
// Mock points writer.
|
||
|
var called int32
|
||
|
s.WritePointsFn = func(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
||
|
atomic.StoreInt32(&called, 1)
|
||
|
|
||
|
if database != "db0" {
|
||
|
t.Fatalf("unexpected database: %s", database)
|
||
|
} else if retentionPolicy != "" {
|
||
|
t.Fatalf("unexpected retention policy: %s", retentionPolicy)
|
||
|
} else if !reflect.DeepEqual(points, []models.Point{
|
||
|
models.MustNewPoint(
|
||
|
"sys.cpu.user",
|
||
|
models.NewTags(map[string]string{"host": "webserver01", "cpu": "0"}),
|
||
|
map[string]interface{}{"value": 42.5},
|
||
|
time.Unix(1356998400, 0),
|
||
|
),
|
||
|
}) {
|
||
|
t.Fatalf("unexpected points: %#v", points)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Open connection to the service.
|
||
|
conn, err := net.Dial("tcp", s.Service.Addr().String())
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
defer conn.Close()
|
||
|
|
||
|
// Write telnet data and close.
|
||
|
if _, err := conn.Write([]byte("put sys.cpu.user 1356998400 42.5 host=webserver01 cpu=0")); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if err := conn.Close(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
tick := time.Tick(10 * time.Millisecond)
|
||
|
timeout := time.After(10 * time.Second)
|
||
|
|
||
|
for {
|
||
|
select {
|
||
|
case <-tick:
|
||
|
// Verify that the writer was called.
|
||
|
if atomic.LoadInt32(&called) > 0 {
|
||
|
return
|
||
|
}
|
||
|
case <-timeout:
|
||
|
t.Fatal("points writer not called")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Ensure a point can be written via the HTTP protocol.
|
||
|
func TestService_HTTP(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
s := NewTestService("db0", "127.0.0.1:0")
|
||
|
if err := s.Service.Open(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
defer s.Service.Close()
|
||
|
|
||
|
// Mock points writer.
|
||
|
var called bool
|
||
|
s.WritePointsFn = func(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
||
|
called = true
|
||
|
if database != "db0" {
|
||
|
t.Fatalf("unexpected database: %s", database)
|
||
|
} else if retentionPolicy != "" {
|
||
|
t.Fatalf("unexpected retention policy: %s", retentionPolicy)
|
||
|
} else if !reflect.DeepEqual(points, []models.Point{
|
||
|
models.MustNewPoint(
|
||
|
"sys.cpu.nice",
|
||
|
models.NewTags(map[string]string{"dc": "lga", "host": "web01"}),
|
||
|
map[string]interface{}{"value": 18.0},
|
||
|
time.Unix(1346846400, 0),
|
||
|
),
|
||
|
}) {
|
||
|
spew.Dump(points)
|
||
|
t.Fatalf("unexpected points: %#v", points)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Write HTTP request to server.
|
||
|
resp, err := http.Post("http://"+s.Service.Addr().String()+"/api/put", "application/json", strings.NewReader(`{"metric":"sys.cpu.nice", "timestamp":1346846400, "value":18, "tags":{"host":"web01", "dc":"lga"}}`))
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
// Verify status and body.
|
||
|
if resp.StatusCode != http.StatusNoContent {
|
||
|
t.Fatalf("unexpected status code: %d", resp.StatusCode)
|
||
|
}
|
||
|
|
||
|
// Verify that the writer was called.
|
||
|
if !called {
|
||
|
t.Fatal("points writer not called")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type TestService struct {
|
||
|
Service *Service
|
||
|
MetaClient *internal.MetaClientMock
|
||
|
WritePointsFn func(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error
|
||
|
}
|
||
|
|
||
|
// NewTestService returns a new instance of Service.
|
||
|
func NewTestService(database string, bind string) *TestService {
|
||
|
s, err := NewService(Config{
|
||
|
BindAddress: bind,
|
||
|
Database: database,
|
||
|
ConsistencyLevel: "one",
|
||
|
})
|
||
|
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
service := &TestService{
|
||
|
Service: s,
|
||
|
MetaClient: &internal.MetaClientMock{},
|
||
|
}
|
||
|
|
||
|
service.MetaClient.CreateDatabaseFn = func(db string) (*meta.DatabaseInfo, error) {
|
||
|
if got, exp := db, database; got != exp {
|
||
|
return nil, fmt.Errorf("got %v, expected %v", got, exp)
|
||
|
}
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
if testing.Verbose() {
|
||
|
service.Service.WithLogger(zap.New(
|
||
|
zap.NewTextEncoder(),
|
||
|
zap.Output(os.Stderr),
|
||
|
))
|
||
|
}
|
||
|
|
||
|
service.Service.MetaClient = service.MetaClient
|
||
|
service.Service.PointsWriter = service
|
||
|
return service
|
||
|
}
|
||
|
|
||
|
func (s *TestService) WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
|
||
|
return s.WritePointsFn(database, retentionPolicy, consistencyLevel, points)
|
||
|
}
|