906 lines
29 KiB
Go
906 lines
29 KiB
Go
package influxdb
|
|
|
|
/*
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
libhttp "net/http"
|
|
"net/url"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/influxdb/influxdb/cluster"
|
|
. "github.com/influxdb/influxdb/common"
|
|
"github.com/influxdb/influxdb/configuration"
|
|
"github.com/influxdb/influxdb/coordinator"
|
|
"github.com/influxdb/influxdb/engine"
|
|
"github.com/influxdb/influxdb/parser"
|
|
"github.com/influxdb/influxdb/protocol"
|
|
. "launchpad.net/gocheck"
|
|
)
|
|
|
|
// Hook up gocheck into the gotest runner.
|
|
func Test(t *testing.T) {
|
|
TestingT(t)
|
|
}
|
|
|
|
type ApiSuite struct {
|
|
listener net.Listener
|
|
server *HttpServer
|
|
coordinator *MockCoordinator
|
|
manager *MockUserManager
|
|
}
|
|
|
|
var _ = Suite(&ApiSuite{})
|
|
|
|
func (self *MockCoordinator) RunQuery(_ User, _ string, query string, yield engine.Processor) error {
|
|
if self.returnedError != nil {
|
|
return self.returnedError
|
|
}
|
|
|
|
series, err := StringToSeriesArray(`
|
|
[
|
|
{
|
|
"points": [
|
|
{
|
|
"values": [
|
|
{ "string_value": "some_value"},{"is_null": true}
|
|
],
|
|
"timestamp": 1381346631000000,
|
|
"sequence_number": 1
|
|
},
|
|
{
|
|
"values": [
|
|
{"string_value": "some_value"},{"int64_value": 2}
|
|
],
|
|
"timestamp": 1381346632000000,
|
|
"sequence_number": 2
|
|
}
|
|
],
|
|
"name": "foo",
|
|
"fields": ["column_one", "column_two"]
|
|
},
|
|
{
|
|
"points": [
|
|
{
|
|
"values": [
|
|
{ "string_value": "some_value"},{"int64_value": 3}
|
|
],
|
|
"timestamp": 1381346633000000,
|
|
"sequence_number": 1
|
|
},
|
|
{
|
|
"values": [
|
|
{"string_value": "some_value"},{"int64_value": 4}
|
|
],
|
|
"timestamp": 1381346634000000,
|
|
"sequence_number": 2
|
|
}
|
|
],
|
|
"name": "foo",
|
|
"fields": ["column_one", "column_two"]
|
|
}
|
|
]
|
|
`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := yield.Yield(series[0]); err != nil {
|
|
return err
|
|
}
|
|
_, err = yield.Yield(series[1])
|
|
return err
|
|
}
|
|
|
|
type MockCoordinator struct {
|
|
coordinator.Coordinator
|
|
series []*protocol.Series
|
|
continuousQueries map[string][]*cluster.ContinuousQuery
|
|
deleteQueries []*parser.DeleteQuery
|
|
db string
|
|
droppedDb string
|
|
returnedError error
|
|
}
|
|
|
|
func (self *MockCoordinator) WriteSeriesData(_ User, db string, series []*protocol.Series) error {
|
|
self.series = append(self.series, series...)
|
|
return nil
|
|
}
|
|
|
|
func (self *MockCoordinator) DeleteSeriesData(_ User, db string, query *parser.DeleteQuery, localOnly bool) error {
|
|
self.deleteQueries = append(self.deleteQueries, query)
|
|
return nil
|
|
}
|
|
|
|
func (self *MockCoordinator) CreateDatabase(_ User, db string) error {
|
|
self.db = db
|
|
return nil
|
|
}
|
|
|
|
func (self *MockCoordinator) ListDatabases(_ User) ([]*cluster.Database, error) {
|
|
return []*cluster.Database{{"db1"}, {"db2"}}, nil
|
|
}
|
|
|
|
func (self *MockCoordinator) DropDatabase(_ User, db string) error {
|
|
self.droppedDb = db
|
|
return nil
|
|
}
|
|
|
|
func (self *MockCoordinator) ListContinuousQueries(_ User, db string) ([]*protocol.Series, error) {
|
|
points := []*protocol.Point{}
|
|
|
|
for _, query := range self.continuousQueries[db] {
|
|
queryId := int64(query.Id)
|
|
queryString := query.Query
|
|
points = append(points, &protocol.Point{
|
|
Values: []*protocol.FieldValue{
|
|
{Int64Value: &queryId},
|
|
{StringValue: &queryString},
|
|
},
|
|
Timestamp: nil,
|
|
SequenceNumber: nil,
|
|
})
|
|
}
|
|
|
|
seriesName := "continuous queries"
|
|
series := []*protocol.Series{{
|
|
Name: &seriesName,
|
|
Fields: []string{"id", "query"},
|
|
Points: points,
|
|
}}
|
|
return series, nil
|
|
}
|
|
|
|
func (self *MockCoordinator) CreateContinuousQuery(_ User, db string, query string) error {
|
|
self.continuousQueries[db] = append(self.continuousQueries[db], &cluster.ContinuousQuery{2, query})
|
|
return nil
|
|
}
|
|
|
|
func (self *MockCoordinator) DeleteContinuousQuery(_ User, db string, id uint32) error {
|
|
length := len(self.continuousQueries[db])
|
|
_, self.continuousQueries[db] = self.continuousQueries[db][length-1], self.continuousQueries[db][:length-1]
|
|
return nil
|
|
}
|
|
|
|
func (self *ApiSuite) formatUrl(path string, args ...interface{}) string {
|
|
path = fmt.Sprintf(path, args...)
|
|
port := self.listener.Addr().(*net.TCPAddr).Port
|
|
return fmt.Sprintf("http://localhost:%d%s", port, path)
|
|
}
|
|
|
|
func (self *ApiSuite) SetUpSuite(c *C) {
|
|
self.coordinator = &MockCoordinator{
|
|
continuousQueries: map[string][]*cluster.ContinuousQuery{
|
|
"db1": {
|
|
{1, "select * from foo into bar;"},
|
|
},
|
|
},
|
|
}
|
|
|
|
self.manager = &MockUserManager{
|
|
clusterAdmins: []string{"root"},
|
|
dbUsers: map[string]map[string]MockDbUser{"db1": {"db_user1": {Name: "db_user1", IsAdmin: false}}},
|
|
}
|
|
config := &configuration.Configuration{
|
|
ApiReadTimeout: 10 * time.Second,
|
|
}
|
|
self.server = NewHttpServer(
|
|
config,
|
|
self.coordinator,
|
|
self.manager,
|
|
cluster.NewClusterConfiguration(config, nil, nil, nil, nil),
|
|
nil)
|
|
var err error
|
|
self.listener, err = net.Listen("tcp4", ":8081")
|
|
c.Assert(err, IsNil)
|
|
go func() {
|
|
self.server.Serve(self.listener)
|
|
}()
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
|
|
func (self *ApiSuite) TearDownSuite(c *C) {
|
|
self.server.Close()
|
|
}
|
|
|
|
func (self *ApiSuite) SetUpTest(c *C) {
|
|
self.coordinator.series = nil
|
|
self.coordinator.returnedError = nil
|
|
self.manager.ops = nil
|
|
}
|
|
|
|
func (self *ApiSuite) TestHealthCheck(c *C) {
|
|
url := self.formatUrl("/ping")
|
|
resp, err := libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
resp.Body.Close()
|
|
}
|
|
|
|
func (self *ApiSuite) TestClusterAdminAuthentication(c *C) {
|
|
url := self.formatUrl("/cluster_admins/authenticate?u=root&p=root")
|
|
resp, err := libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
resp.Body.Close()
|
|
|
|
url = self.formatUrl("/cluster_admins/authenticate?u=fail_auth&p=anypass")
|
|
resp, err = libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusUnauthorized)
|
|
c.Assert(resp.Header.Get("WWW-Authenticate"), Equals, "Basic realm=\"influxdb\"")
|
|
resp.Body.Close()
|
|
}
|
|
|
|
func (self *ApiSuite) TestDbUserAuthentication(c *C) {
|
|
url := self.formatUrl("/db/foo/authenticate?u=dbuser&p=password")
|
|
resp, err := libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
resp.Body.Close()
|
|
|
|
url = self.formatUrl("/db/foo/authenticate?u=fail_auth&p=anypass")
|
|
resp, err = libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusUnauthorized)
|
|
c.Assert(resp.Header.Get("WWW-Authenticate"), Equals, "Basic realm=\"influxdb\"")
|
|
resp.Body.Close()
|
|
}
|
|
|
|
func (self *ApiSuite) TestDbUserBasicAuthentication(c *C) {
|
|
url := self.formatUrl("/db/foo/authenticate")
|
|
req, err := libhttp.NewRequest("GET", url, nil)
|
|
auth := base64.StdEncoding.EncodeToString([]byte("dbuser:password"))
|
|
req.Header.Add("Authorization", "Basic "+auth)
|
|
c.Assert(err, IsNil)
|
|
resp, err := libhttp.DefaultClient.Do(req)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
resp.Body.Close()
|
|
}
|
|
|
|
func (self *ApiSuite) TestQueryAsClusterAdmin(c *C) {
|
|
query := "select * from foo;"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&u=root&p=root", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
}
|
|
|
|
func (self *ApiSuite) TestQueryWithNullColumns(c *C) {
|
|
query := "select * from foo;"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&time_precision=s&u=dbuser&p=password", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
series := []SerializedSeries{}
|
|
err = json.Unmarshal(data, &series)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(series, HasLen, 1)
|
|
c.Assert(series[0].Name, Equals, "foo")
|
|
// time, seq, column_one, column_two
|
|
c.Assert(series[0].Columns, HasLen, 4)
|
|
c.Assert(series[0].Points, HasLen, 4)
|
|
c.Assert(int(series[0].Points[0][0].(float64)), Equals, 1381346631)
|
|
c.Assert(series[0].Points[0][3], Equals, nil)
|
|
}
|
|
|
|
func (self *ApiSuite) TestQueryErrorPropagatesProperly(c *C) {
|
|
self.coordinator.returnedError = fmt.Errorf("some error")
|
|
query := "select * from does_not_exist;"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&time_precision=s&u=dbuser&p=password", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusBadRequest)
|
|
}
|
|
|
|
func (self *ApiSuite) TestUnauthorizedErrorWithCompression(c *C) {
|
|
addr := self.formatUrl("/cluster_admins/authenticate?u=fail_auth&p=invalidpassword")
|
|
req, err := libhttp.NewRequest("GET", addr, nil)
|
|
c.Assert(err, IsNil)
|
|
req.Header.Set("Accept-Encoding", "gzip")
|
|
resp, err := libhttp.DefaultClient.Do(req)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusUnauthorized)
|
|
c.Assert(resp.Header.Get("Content-Type"), Equals, "text/plain")
|
|
c.Assert(resp.Header.Get("Content-Encoding"), Equals, "gzip")
|
|
}
|
|
|
|
func (self *ApiSuite) TestQueryWithSecondsPrecision(c *C) {
|
|
query := "select * from foo where column_one == 'some_value';"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&time_precision=s&u=dbuser&p=password", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
series := []SerializedSeries{}
|
|
err = json.Unmarshal(data, &series)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(series, HasLen, 1)
|
|
c.Assert(series[0].Name, Equals, "foo")
|
|
// time, seq, column_one, column_two
|
|
c.Assert(series[0].Columns, HasLen, 4)
|
|
c.Assert(series[0].Points, HasLen, 4)
|
|
c.Assert(int(series[0].Points[0][0].(float64)), Equals, 1381346631)
|
|
}
|
|
|
|
func (self *ApiSuite) TestQueryWithInvalidPrecision(c *C) {
|
|
query := "select * from foo where column_one == 'some_value';"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&time_precision=foo&u=dbuser&p=password", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusBadRequest)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "text/plain")
|
|
}
|
|
|
|
func (self *ApiSuite) TestNotChunkedQuery(c *C) {
|
|
query := "select * from foo where column_one == 'some_value';"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&u=dbuser&p=password", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
series := []SerializedSeries{}
|
|
err = json.Unmarshal(data, &series)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(series, HasLen, 1)
|
|
c.Assert(series[0].Name, Equals, "foo")
|
|
// time, seq, column_one, column_two
|
|
c.Assert(series[0].Columns, HasLen, 4)
|
|
c.Assert(series[0].Points, HasLen, 4)
|
|
// timestamp precision is milliseconds by default
|
|
c.Assert(int64(series[0].Points[0][0].(float64)), Equals, int64(1381346631000))
|
|
}
|
|
|
|
func (self *ApiSuite) TestNotChunkedPrettyQuery(c *C) {
|
|
query := "select * from foo where column_one == 'some_value';"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&u=dbuser&p=password&pretty=true", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
series := []SerializedSeries{}
|
|
err = json.Unmarshal(data, &series)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(series, HasLen, 1)
|
|
c.Assert(series[0].Name, Equals, "foo")
|
|
// time, seq, column_one, column_two
|
|
c.Assert(series[0].Columns, HasLen, 4)
|
|
c.Assert(series[0].Points, HasLen, 4)
|
|
// timestamp precision is milliseconds by default
|
|
c.Assert(int64(series[0].Points[0][0].(float64)), Equals, int64(1381346631000))
|
|
}
|
|
|
|
func (self *ApiSuite) TestNotChunkedNotPrettyQuery(c *C) {
|
|
query := "select * from foo where column_one == 'some_value';"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&u=dbuser&p=password&pretty=false", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
series := []SerializedSeries{}
|
|
err = json.Unmarshal(data, &series)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(series, HasLen, 1)
|
|
c.Assert(series[0].Name, Equals, "foo")
|
|
// time, seq, column_one, column_two
|
|
c.Assert(series[0].Columns, HasLen, 4)
|
|
c.Assert(series[0].Points, HasLen, 4)
|
|
// timestamp precision is milliseconds by default
|
|
c.Assert(int64(series[0].Points[0][0].(float64)), Equals, int64(1381346631000))
|
|
}
|
|
|
|
func (self *ApiSuite) TestChunkedQuery(c *C) {
|
|
query := "select * from foo where column_one == 'some_value';"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&chunked=true&u=dbuser&p=password", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
|
|
for i := 0; i < 2; i++ {
|
|
chunk := make([]byte, 2048, 2048)
|
|
n, err := resp.Body.Read(chunk)
|
|
|
|
series := SerializedSeries{}
|
|
err = json.Unmarshal(chunk[0:n], &series)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(series.Name, Equals, "foo")
|
|
// time, seq, column_one, column_two
|
|
c.Assert(series.Columns, HasLen, 4)
|
|
// each chunk should have 2 points
|
|
c.Assert(series.Points, HasLen, 2)
|
|
}
|
|
}
|
|
|
|
func (self *ApiSuite) TestPrettyChunkedQuery(c *C) {
|
|
query := "select * from foo where column_one == 'some_value';"
|
|
query = url.QueryEscape(query)
|
|
addr := self.formatUrl("/db/foo/series?q=%s&chunked=true&u=dbuser&p=password&pretty=true", query)
|
|
resp, err := libhttp.Get(addr)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
|
|
for i := 0; i < 2; i++ {
|
|
chunk := make([]byte, 2048, 2048)
|
|
n, err := resp.Body.Read(chunk)
|
|
|
|
series := SerializedSeries{}
|
|
err = json.Unmarshal(chunk[0:n], &series)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(series.Name, Equals, "foo")
|
|
// time, seq, column_one, column_two
|
|
c.Assert(series.Columns, HasLen, 4)
|
|
// each chunk should have 2 points
|
|
c.Assert(series.Points, HasLen, 2)
|
|
}
|
|
}
|
|
|
|
func (self *ApiSuite) TestWriteDataWithTimeInSeconds(c *C) {
|
|
data := `
|
|
[
|
|
{
|
|
"points": [
|
|
[1382131686, "1"]
|
|
],
|
|
"name": "foo",
|
|
"columns": ["time", "column_one"]
|
|
}
|
|
]
|
|
`
|
|
|
|
addr := self.formatUrl("/db/foo/series?time_precision=s&u=dbuser&p=password")
|
|
resp, err := libhttp.Post(addr, "application/json", bytes.NewBufferString(data))
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.coordinator.series, HasLen, 1)
|
|
series := self.coordinator.series[0]
|
|
|
|
// check the types
|
|
c.Assert(series.Fields, HasLen, 1)
|
|
c.Assert(series.Fields[0], Equals, "column_one")
|
|
|
|
// check the values
|
|
c.Assert(series.Points, HasLen, 1)
|
|
c.Assert(*series.Points[0].Values[0].StringValue, Equals, "1")
|
|
c.Assert(*series.Points[0].GetTimestampInMicroseconds(), Equals, int64(1382131686000000))
|
|
}
|
|
|
|
func (self *ApiSuite) TestWriteDataWithTime(c *C) {
|
|
data := `
|
|
[
|
|
{
|
|
"points": [
|
|
[1382131686000, "1"]
|
|
],
|
|
"name": "foo",
|
|
"columns": ["time", "column_one"]
|
|
}
|
|
]
|
|
`
|
|
|
|
addr := self.formatUrl("/db/foo/series?u=dbuser&p=password")
|
|
resp, err := libhttp.Post(addr, "application/json", bytes.NewBufferString(data))
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.coordinator.series, HasLen, 1)
|
|
series := self.coordinator.series[0]
|
|
|
|
// check the types
|
|
c.Assert(series.Fields, HasLen, 1)
|
|
c.Assert(series.Fields[0], Equals, "column_one")
|
|
|
|
// check the values
|
|
c.Assert(series.Points, HasLen, 1)
|
|
c.Assert(*series.Points[0].Values[0].StringValue, Equals, "1")
|
|
c.Assert(*series.Points[0].GetTimestampInMicroseconds(), Equals, int64(1382131686000000))
|
|
}
|
|
|
|
func (self *ApiSuite) TestWriteDataWithInvalidTime(c *C) {
|
|
data := `
|
|
[
|
|
{
|
|
"points": [
|
|
["foo", "1"]
|
|
],
|
|
"name": "foo",
|
|
"columns": ["time", "column_one"]
|
|
}
|
|
]
|
|
`
|
|
|
|
addr := self.formatUrl("/db/foo/series?u=dbuser&p=password")
|
|
resp, err := libhttp.Post(addr, "application/json", bytes.NewBufferString(data))
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusBadRequest)
|
|
}
|
|
|
|
func (self *ApiSuite) TestWriteDataWithNull(c *C) {
|
|
data := `
|
|
[
|
|
{
|
|
"points": [
|
|
["1", 1, 1.0, true],
|
|
["2", 2, 2.0, false],
|
|
["3", 3, 3.0, null]
|
|
],
|
|
"name": "foo",
|
|
"columns": ["column_one", "column_two", "column_three", "column_four"]
|
|
}
|
|
]
|
|
`
|
|
|
|
addr := self.formatUrl("/db/foo/series?u=dbuser&p=password")
|
|
resp, err := libhttp.Post(addr, "application/json", bytes.NewBufferString(data))
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.coordinator.series, HasLen, 1)
|
|
series := self.coordinator.series[0]
|
|
c.Assert(series.Fields, HasLen, 4)
|
|
|
|
// check the types
|
|
c.Assert(series.Fields[0], Equals, "column_one")
|
|
c.Assert(series.Fields[1], Equals, "column_two")
|
|
c.Assert(series.Fields[2], Equals, "column_three")
|
|
c.Assert(series.Fields[3], Equals, "column_four")
|
|
|
|
// check the values
|
|
c.Assert(series.Points, HasLen, 3)
|
|
c.Assert(*series.Points[2].Values[0].StringValue, Equals, "3")
|
|
c.Assert(*series.Points[2].Values[1].Int64Value, Equals, int64(3))
|
|
c.Assert(*series.Points[2].Values[2].DoubleValue, Equals, 3.0)
|
|
c.Assert(series.Points[2].Values[3].GetIsNull(), Equals, true)
|
|
}
|
|
|
|
func (self *ApiSuite) TestWriteData(c *C) {
|
|
data := `
|
|
[
|
|
{
|
|
"points": [
|
|
["1", 1, 1.0, true],
|
|
["2", 2, 2.0, false],
|
|
["3", 3, 3.0, true]
|
|
],
|
|
"name": "foo",
|
|
"columns": ["column_one", "column_two", "column_three", "column_four"]
|
|
}
|
|
]
|
|
`
|
|
|
|
addr := self.formatUrl("/db/foo/series?u=dbuser&p=password")
|
|
resp, err := libhttp.Post(addr, "application/json", bytes.NewBufferString(data))
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.coordinator.series, HasLen, 1)
|
|
series := self.coordinator.series[0]
|
|
c.Assert(series.Fields, HasLen, 4)
|
|
|
|
// check the types
|
|
c.Assert(series.Fields[0], Equals, "column_one")
|
|
c.Assert(series.Fields[1], Equals, "column_two")
|
|
c.Assert(series.Fields[2], Equals, "column_three")
|
|
c.Assert(series.Fields[3], Equals, "column_four")
|
|
|
|
// check the values
|
|
c.Assert(series.Points, HasLen, 3)
|
|
c.Assert(*series.Points[0].Values[0].StringValue, Equals, "1")
|
|
c.Assert(*series.Points[0].Values[1].Int64Value, Equals, int64(1))
|
|
c.Assert(*series.Points[0].Values[2].DoubleValue, Equals, 1.0)
|
|
c.Assert(*series.Points[0].Values[3].BoolValue, Equals, true)
|
|
}
|
|
|
|
func (self *ApiSuite) TestWriteDataAsClusterAdmin(c *C) {
|
|
data := `
|
|
[
|
|
{
|
|
"points": [
|
|
["1", true]
|
|
],
|
|
"name": "foo",
|
|
"columns": ["column_one", "column_two"]
|
|
}
|
|
]
|
|
`
|
|
|
|
addr := self.formatUrl("/db/foo/series?u=root&p=root")
|
|
resp, err := libhttp.Post(addr, "application/json", bytes.NewBufferString(data))
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
}
|
|
|
|
func (self *ApiSuite) TestCreateDatabase(c *C) {
|
|
data := `{"name": "foo", "apiKey": "bar"}`
|
|
addr := self.formatUrl("/db?api_key=asdf&u=root&p=root")
|
|
resp, err := libhttp.Post(addr, "application/json", bytes.NewBufferString(data))
|
|
c.Assert(err, IsNil)
|
|
_, err = ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusCreated)
|
|
c.Assert(self.coordinator.db, Equals, "foo")
|
|
}
|
|
|
|
func (self *ApiSuite) TestCreateDatabaseNameFailures(c *C) {
|
|
data := map[string]string{
|
|
`{"name": ""}`: "Unable to create database without name",
|
|
`{}`: "Unable to create database without name",
|
|
`{"not_name": "bar"}`: "Unable to create database without name",
|
|
`{"name": " "}`: "Unable to create database without name"}
|
|
for k, v := range data {
|
|
addr := self.formatUrl("/db?u=root&p=root")
|
|
resp, err := libhttp.Post(addr, "application/json", bytes.NewBufferString(k))
|
|
c.Assert(err, IsNil)
|
|
m, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(v, Equals, string(m))
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusBadRequest)
|
|
}
|
|
}
|
|
|
|
func (self *ApiSuite) TestDropDatabase(c *C) {
|
|
addr := self.formatUrl("/db/foo?u=root&p=root")
|
|
req, err := libhttp.NewRequest("DELETE", addr, nil)
|
|
c.Assert(err, IsNil)
|
|
resp, err := libhttp.DefaultClient.Do(req)
|
|
c.Assert(err, IsNil)
|
|
_, err = ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusNoContent)
|
|
c.Assert(self.coordinator.droppedDb, Equals, "foo")
|
|
}
|
|
|
|
func (self *ApiSuite) TestClusterAdminOperations(c *C) {
|
|
url := self.formatUrl("/cluster_admins?u=root&p=root")
|
|
resp, err := libhttp.Post(url, "", bytes.NewBufferString(`{"name":"", "password": "new_pass"}`))
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusBadRequest)
|
|
|
|
url = self.formatUrl("/cluster_admins?u=root&p=root")
|
|
resp, err = libhttp.Post(url, "", bytes.NewBufferString(`{"name":"new_user", "password": "new_pass"}`))
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.manager.ops, HasLen, 1)
|
|
c.Assert(self.manager.ops[0].operation, Equals, "cluster_admin_add")
|
|
c.Assert(self.manager.ops[0].username, Equals, "new_user")
|
|
c.Assert(self.manager.ops[0].password, Equals, "new_pass")
|
|
self.manager.ops = nil
|
|
|
|
url = self.formatUrl("/cluster_admins/new_user?u=root&p=root")
|
|
resp, err = libhttp.Post(url, "", bytes.NewBufferString(`{"password":"new_password"}`))
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.manager.ops, HasLen, 1)
|
|
c.Assert(self.manager.ops[0].operation, Equals, "cluster_admin_passwd")
|
|
c.Assert(self.manager.ops[0].username, Equals, "new_user")
|
|
c.Assert(self.manager.ops[0].password, Equals, "new_password")
|
|
self.manager.ops = nil
|
|
|
|
req, _ := libhttp.NewRequest("DELETE", url, nil)
|
|
resp, err = libhttp.DefaultClient.Do(req)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.manager.ops, HasLen, 1)
|
|
c.Assert(self.manager.ops[0].operation, Equals, "cluster_admin_del")
|
|
c.Assert(self.manager.ops[0].username, Equals, "new_user")
|
|
}
|
|
|
|
func (self *ApiSuite) TestDbUserOperations(c *C) {
|
|
// create user using the `name` field
|
|
url := self.formatUrl("/db/db1/users?u=root&p=root")
|
|
resp, err := libhttp.Post(url, "", bytes.NewBufferString(`{"name":"dbuser", "password": "password"}`))
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.manager.ops, HasLen, 1)
|
|
c.Assert(self.manager.ops[0].operation, Equals, "db_user_add")
|
|
c.Assert(self.manager.ops[0].username, Equals, "dbuser")
|
|
c.Assert(self.manager.ops[0].password, Equals, "password")
|
|
self.manager.ops = nil
|
|
|
|
url = self.formatUrl("/db/db1/users/dbuser?u=root&p=root")
|
|
resp, err = libhttp.Post(url, "", bytes.NewBufferString(`{"password":"new_password"}`))
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.manager.ops, HasLen, 1)
|
|
c.Assert(self.manager.ops[0].operation, Equals, "db_user_passwd")
|
|
c.Assert(self.manager.ops[0].username, Equals, "dbuser")
|
|
c.Assert(self.manager.ops[0].password, Equals, "new_password")
|
|
self.manager.ops = nil
|
|
|
|
// empty usernames aren't valid
|
|
url = self.formatUrl("/db/db1/users?u=root&p=root")
|
|
resp, err = libhttp.Post(url, "", bytes.NewBufferString(`{"name":"", "password": "password"}`))
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusBadRequest)
|
|
|
|
// Fix #477 - Username should support @ character - https://github.com/influxdb/influxdb/issues/447
|
|
url = self.formatUrl("/db/db1/users?u=root&p=root")
|
|
resp, err = libhttp.Post(url, "", bytes.NewBufferString(`{"name":"paul@influxdb.com", "password": "password"}`))
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.manager.ops, HasLen, 1)
|
|
c.Assert(self.manager.ops[0].operation, Equals, "db_user_add")
|
|
c.Assert(self.manager.ops[0].username, Equals, "paul@influxdb.com")
|
|
c.Assert(self.manager.ops[0].password, Equals, "password")
|
|
self.manager.ops = nil
|
|
|
|
// set and unset the db admin flag
|
|
url = self.formatUrl("/db/db1/users/dbuser?u=root&p=root")
|
|
resp, err = libhttp.Post(url, "", bytes.NewBufferString(`{"admin": true}`))
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.manager.ops, HasLen, 1)
|
|
c.Assert(self.manager.ops[0].operation, Equals, "db_user_admin")
|
|
c.Assert(self.manager.ops[0].username, Equals, "dbuser")
|
|
c.Assert(self.manager.ops[0].isAdmin, Equals, true)
|
|
self.manager.ops = nil
|
|
url = self.formatUrl("/db/db1/users/dbuser?u=root&p=root")
|
|
resp, err = libhttp.Post(url, "", bytes.NewBufferString(`{"admin": false}`))
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.manager.ops, HasLen, 1)
|
|
c.Assert(self.manager.ops[0].operation, Equals, "db_user_admin")
|
|
c.Assert(self.manager.ops[0].username, Equals, "dbuser")
|
|
c.Assert(self.manager.ops[0].isAdmin, Equals, false)
|
|
self.manager.ops = nil
|
|
|
|
url = self.formatUrl("/db/db1/users/dbuser?u=root&p=root")
|
|
req, _ := libhttp.NewRequest("DELETE", url, nil)
|
|
resp, err = libhttp.DefaultClient.Do(req)
|
|
c.Assert(err, IsNil)
|
|
defer resp.Body.Close()
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
c.Assert(self.manager.ops, HasLen, 1)
|
|
c.Assert(self.manager.ops[0].operation, Equals, "db_user_del")
|
|
c.Assert(self.manager.ops[0].username, Equals, "dbuser")
|
|
}
|
|
|
|
func (self *ApiSuite) TestClusterAdminsIndex(c *C) {
|
|
url := self.formatUrl("/cluster_admins?u=root&p=root")
|
|
resp, err := libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
users := []*ApiUser{}
|
|
err = json.Unmarshal(body, &users)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(users, DeepEquals, []*ApiUser{{"root"}})
|
|
}
|
|
|
|
func (self *ApiSuite) TestPrettyClusterAdminsIndex(c *C) {
|
|
url := self.formatUrl("/cluster_admins?u=root&p=root&pretty=true")
|
|
resp, err := libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
users := []*ApiUser{}
|
|
err = json.Unmarshal(body, &users)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(users, DeepEquals, []*ApiUser{{"root"}})
|
|
}
|
|
|
|
func (self *ApiSuite) TestDbUsersIndex(c *C) {
|
|
url := self.formatUrl("/db/db1/users?u=root&p=root")
|
|
resp, err := libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
users := []*UserDetail{}
|
|
err = json.Unmarshal(body, &users)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(users, HasLen, 1)
|
|
c.Assert(users[0], DeepEquals, &UserDetail{"db_user1", false, ".*", ".*"})
|
|
}
|
|
|
|
func (self *ApiSuite) TestPrettyDbUsersIndex(c *C) {
|
|
url := self.formatUrl("/db/db1/users?u=root&p=root&pretty=true")
|
|
resp, err := libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
users := []*UserDetail{}
|
|
err = json.Unmarshal(body, &users)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(users, HasLen, 1)
|
|
c.Assert(users[0], DeepEquals, &UserDetail{"db_user1", false, ".*", ".*"})
|
|
}
|
|
|
|
func (self *ApiSuite) TestDbUserShow(c *C) {
|
|
url := self.formatUrl("/db/db1/users/db_user1?u=root&p=root")
|
|
resp, err := libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
userDetail := &UserDetail{}
|
|
err = json.Unmarshal(body, &userDetail)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(userDetail, DeepEquals, &UserDetail{"db_user1", false, ".*", ".*"})
|
|
}
|
|
|
|
func (self *ApiSuite) TestDatabasesIndex(c *C) {
|
|
for _, path := range []string{"/db?u=root&p=root", "/db?u=root&p=root"} {
|
|
url := self.formatUrl(path)
|
|
resp, err := libhttp.Get(url)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.Header.Get("content-type"), Equals, "application/json")
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
databases := []*cluster.Database{}
|
|
err = json.Unmarshal(body, &databases)
|
|
c.Assert(err, IsNil)
|
|
err = json.Unmarshal(body, &databases)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(databases, DeepEquals, []*cluster.Database{{"db1"}, {"db2"}})
|
|
}
|
|
}
|
|
|
|
func (self *ApiSuite) TestBasicAuthentication(c *C) {
|
|
url := self.formatUrl("/db")
|
|
req, err := libhttp.NewRequest("GET", url, nil)
|
|
c.Assert(err, IsNil)
|
|
auth := base64.StdEncoding.EncodeToString([]byte("root:root"))
|
|
req.Header.Add("Authorization", "Basic "+auth)
|
|
resp, err := libhttp.DefaultClient.Do(req)
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(resp.StatusCode, Equals, libhttp.StatusOK)
|
|
databases := []*cluster.Database{}
|
|
c.Assert(err, IsNil)
|
|
err = json.Unmarshal(body, &databases)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(databases, DeepEquals, []*cluster.Database{{"db1"}, {"db2"}})
|
|
}
|
|
*/
|