diff --git a/.circleci/config.yml b/.circleci/config.yml index 76327830..ed1f9032 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,4 @@ -# Golang CircleCI 2.0 configuration file +# Golang CircleCI 2.1 configuration file # # Check https://circleci.com/docs/2.0/language-go/ for more details version: 2.1 @@ -8,22 +8,34 @@ commands: - run: name: "Post onBoarding request to InfluxDB 2" command: ./scripts/influxdb-onboarding.sh + collect-coverage-reports: + steps: + - run: + name: Collecting coverage reports + command: | + curl -Os https://uploader.codecov.io/latest/linux/codecov + curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM + curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig + curl -s https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import + gpgv codecov.SHA256SUM.sig codecov.SHA256SUM + shasum -a 256 -c codecov.SHA256SUM + chmod +x ./codecov + ./codecov jobs: - build: + lint: docker: # specify the version - - image: cimg/go:1.17.2 + - image: cimg/go:1.17 environment: ENV: CI GO111MODULE: "on" steps: - checkout - run: go get -v -t -d ./... - - run: go vet ./... - - run: go install honnef.co/go/tools/cmd/staticcheck@latest && staticcheck --checks="all" ./... + - run: make lint tests: docker: - - image: cimg/go:1.17.2 + - image: cimg/go:1.17 environment: ENV: CI GO111MODULE: "on" @@ -40,16 +52,8 @@ jobs: steps: - checkout - influxdb-onboarding - - run: - name: "Create a temp directory for artifacts" - command: | - mkdir -p /tmp/artifacts - mkdir -p /tmp/test-results - - run: - command: | - gotestsum --junitfile /tmp/test-results/unit-tests.xml -- -race -tags=e2e -coverprofile=coverage.txt -covermode=atomic ./... - bash <(curl -s https://codecov.io/bash) - go tool cover -html=coverage.txt -o /tmp/artifacts/coverage.html + - run: make coverage + - collect-coverage-reports - store_artifacts: path: /tmp/artifacts - store_artifacts: @@ -61,7 +65,7 @@ workflows: version: 2 build-test: jobs: - - build + - lint - tests: requires: - - build \ No newline at end of file + - lint \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100755 index 00000000..f114d15a --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +artifacts_path := /tmp/artifacts + +help: + @echo 'Targets:' + @echo ' all - runs lint, server, coverage' + @echo ' lint - runs code style checks' + @echo ' shorttest - runs unit and integration tests' + @echo ' test - runs all tests, including e2e tests - requires running influxdb 2 server' + @echo ' coverage - runs all tests, including e2e tests, with coverage report - requires running influxdb 2 server' + @echo ' server - prepares InfluxDB in docker environment' + +lint: + go vet ./... + go install honnef.co/go/tools/cmd/staticcheck@latest && staticcheck --checks='all' --tags e2e ./... + go install golang.org/x/lint/golint@latest && golint ./... + +shorttest: + go test -race -v -count=1 ./... + +test: + go test -race -v -count=1 --tags e2e ./... + +coverage: + go install gotest.tools/gotestsum@latest && gotestsum --junitfile /tmp/test-results/unit-tests.xml -- -race -coverprofile=coverage.txt -covermode=atomic -tags e2e ./... + if test ! -e $(artifacts_path); then mkdir $(artifacts_path); fi + go tool cover -html=coverage.txt -o $(artifacts_path)/coverage.html + +server: + ./scripts/influxdb-restart.sh + +all: lint server coverage diff --git a/influxclient/authorizations_e2e_test.go b/influxclient/authorizations_e2e_test.go index 7e5c4317..721d7a79 100644 --- a/influxclient/authorizations_e2e_test.go +++ b/influxclient/authorizations_e2e_test.go @@ -7,7 +7,6 @@ package influxclient_test import ( - "fmt" "testing" . "github.com/influxdata/influxdb-client-go/influxclient" @@ -56,7 +55,7 @@ func TestAuthorizationsAPI(t *testing.T) { }) require.NoError(t, err) require.NotNil(t, auth) - defer authAPI.Delete(ctx, fmt.Sprintf("%s", *auth.Id)) + defer authAPI.Delete(ctx, safeId(auth.Id)) assert.Equal(t, model.AuthorizationUpdateRequestStatusActive, *auth.Status) auths, err = authAPI.Find(ctx, nil) diff --git a/influxclient/buckets_e2e_test.go b/influxclient/buckets_e2e_test.go index 2e47e261..4872ecef 100644 --- a/influxclient/buckets_e2e_test.go +++ b/influxclient/buckets_e2e_test.go @@ -358,6 +358,12 @@ func TestBucketsAPI_failing(t *testing.T) { assert.Error(t, err) assert.Nil(t, bucket) + bucket, err = bucketsAPI.Update(ctx, &model.Bucket{ + Id: ¬ExistingID, + }) + assert.Error(t, err) + assert.Nil(t, bucket) + bucket, err = bucketsAPI.Create(ctx, &model.Bucket{ OrgID: &invalidID, Name: "bucket-y", diff --git a/influxclient/client.go b/influxclient/client.go index 0f98c526..66498199 100644 --- a/influxclient/client.go +++ b/influxclient/client.go @@ -16,11 +16,13 @@ import ( "net/http" "net/url" "strings" + "time" "github.com/influxdata/influxdb-client-go/influxclient/model" ) const ( + // DefaultBatchSize default batch size used if not set otherwise. DefaultBatchSize = 5000 ) @@ -128,6 +130,100 @@ func (c *Client) APIClient() *model.Client { return c.apiClient } +// DeleteParams holds options for DeletePoints. +type DeleteParams struct { + // Bucket holds bucket name. + Bucket string + // BucketID holds bucket ID. + BucketID string + // Org holds organization name. + Org string + // OrgID holds organization ID. + OrgID string + // Predicate is an expression in delete predicate syntax. + Predicate string + // Start is the earliest time to delete from. + Start time.Time + // Stop is the latest time to delete from. + Stop time.Time +} + +// DeletePoints deletes data from a bucket. +func (c *Client) DeletePoints(ctx context.Context, params *DeleteParams) error { + if params == nil { + return fmt.Errorf("error calling DeletePoints: params cannot be nil") + } + if params.Bucket == "" && params.BucketID == "" { + return fmt.Errorf("error calling DeletePoints: either bucket or bucketID is required") + } + if params.Org == "" && params.OrgID == "" { + return fmt.Errorf("error calling DeletePoints: either org or orgID is required") + } + if params.Start.IsZero() { + return fmt.Errorf("error calling DeletePoints: invalid start time") + } + if params.Stop.IsZero() { + return fmt.Errorf("error calling DeletePoints: invalid stop time") + } + postParams := model.PostDeleteAllParams{ + Body: model.PostDeleteJSONRequestBody{ + Predicate: ¶ms.Predicate, + Start: params.Start, + Stop: params.Stop, + }, + } + if params.Bucket != "" { + postParams.Bucket = ¶ms.Bucket + } else { + postParams.BucketID = ¶ms.BucketID + } + if params.Org != "" { + postParams.Org = ¶ms.Org + } else { + postParams.OrgID = ¶ms.OrgID + } + + err := c.apiClient.PostDelete(ctx, &postParams) + if err != nil { + return fmt.Errorf("error calling DeletePoints: %v", err) + } + return nil +} + +// Ready checks that the server is ready, and reports the duration the instance +// has been up if so. It does not validate authentication parameters. +// See https://docs.influxdata.com/influxdb/v2.0/api/#operation/GetReady. +func (c *Client) Ready(ctx context.Context) (time.Duration, error) { + resp, err := c.apiClient.GetReady(ctx, &model.GetReadyParams{}) + if err != nil { + return 0, fmt.Errorf("error calling Ready: %v", err) + } + up, err := time.ParseDuration(*resp.Up) + if err != nil { + return 0, fmt.Errorf("error calling Ready: %v", err) + } + return up, nil +} + +// Health returns an InfluxDB server health check result. Read the HealthCheck.Status field to get server status. +// Health doesn't validate authentication params. +func (c *Client) Health(ctx context.Context) (*model.HealthCheck, error) { + resp, err := c.apiClient.GetHealth(ctx, &model.GetHealthParams{}) + if err != nil { + return nil, fmt.Errorf("error calling Health: %v", err) + } + return resp, nil +} + +// Ping checks the status and InfluxDB version of the instance. Returns an error if it is not available. +func (c *Client) Ping(ctx context.Context) error { + err := c.apiClient.GetPing(ctx) + if err != nil { + return fmt.Errorf("error calling Ping: %v", err) + } + return nil +} + // makeAPICall issues an HTTP request to InfluxDB server API url according to parameters. // Additionally, sets Authorization header and User-Agent. // It returns http.Response or error. Error can be a *ServerError if server responded with error. diff --git a/influxclient/client_e2e_test.go b/influxclient/client_e2e_test.go new file mode 100644 index 00000000..adc4dd01 --- /dev/null +++ b/influxclient/client_e2e_test.go @@ -0,0 +1,110 @@ +// +build e2e + +// Copyright 2020-2021 InfluxData, Inc. All rights reserved. +// Use of this source code is governed by MIT +// license that can be found in the LICENSE file. + +package influxclient_test + +import ( + "github.com/influxdata/influxdb-client-go/influxclient" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestReady(t *testing.T) { + client, ctx := newClient(t) + + up, err := client.Ready(ctx) + require.NoError(t, err) + assert.NotZero(t, up) +} + +func TestHealth(t *testing.T) { + client, ctx := newClient(t) + + health, err := client.Health(ctx) + require.NoError(t, err) + assert.NotNil(t, health) + assert.NotEmpty(t, health.Name) + assert.Equal(t, "influxdb", health.Name) + assert.NotEmpty(t, health.Status) + assert.Equal(t, "pass", string(health.Status)) + assert.NotEmpty(t, health.Commit) + assert.NotEmpty(t, health.Version) +} + +func TestPing(t *testing.T) { + client, ctx := newClient(t) + + err := client.Ping(ctx) + require.NoError(t, err) +} + +func TestDeletePoints(t *testing.T) { + client, ctx := newClient(t) + + err := client.DeletePoints(ctx, nil) + assert.Error(t, err) + + err = client.DeletePoints(ctx, &influxclient.DeleteParams{}) + assert.Error(t, err) + + err = client.DeletePoints(ctx, &influxclient.DeleteParams{ + Org: orgName, + }) + assert.Error(t, err) + + err = client.DeletePoints(ctx, &influxclient.DeleteParams{ + Bucket: bucketName, + }) + assert.Error(t, err) + + err = client.DeletePoints(ctx, &influxclient.DeleteParams{ + Org: orgName, + Bucket: bucketName, + }) + assert.Error(t, err) + + err = client.DeletePoints(ctx, &influxclient.DeleteParams{ + Org: orgName, + Bucket: bucketName, + Start: time.Now(), + }) + assert.Error(t, err) + + err = client.DeletePoints(ctx, &influxclient.DeleteParams{ + Org: orgName, + Bucket: bucketName, + Stop: time.Now(), + }) + assert.Error(t, err) + + err = client.DeletePoints(ctx, &influxclient.DeleteParams{ + Org: orgName, + Bucket: bucketName, + // without predicate + Start: time.Now().AddDate(0, 0, -1), + Stop: time.Now(), + }) + assert.NoError(t, err) + + org, err := client.OrganizationAPI().FindOne(ctx, &influxclient.Filter{Name: orgName}) + require.NoError(t, err) + require.NotNil(t, org) + bucket, err := client.BucketsAPI().FindOne(ctx, &influxclient.Filter{Name: bucketName}) + require.NoError(t, err) + require.NotNil(t, bucket) + + err = client.DeletePoints(ctx, &influxclient.DeleteParams{ + OrgID: *org.Id, + BucketID: *bucket.Id, + Predicate: `_measurement="sensorData"`, + Start: time.Now().AddDate(0, 0, -1), + Stop: time.Now(), + }) + assert.NoError(t, err) +} diff --git a/influxclient/client_test.go b/influxclient/client_test.go index 4015e00b..14583735 100644 --- a/influxclient/client_test.go +++ b/influxclient/client_test.go @@ -11,6 +11,7 @@ import ( "net/url" "strings" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,6 +22,10 @@ func TestNew(t *testing.T) { require.Error(t, err) assert.Equal(t, "empty server URL", err.Error()) + _, err = New(Params{ServerURL: "http@localhost:8086"}) + require.Error(t, err) + assert.Equal(t, "error parsing server URL: parse \"http@localhost:8086/\": first path segment in URL cannot contain colon", err.Error()) + c, err := New(Params{ServerURL: "http://localhost:8086"}) require.NoError(t, err) assert.Equal(t, "http://localhost:8086", c.params.ServerURL) @@ -151,3 +156,239 @@ func TestResolveErrorNoError(t *testing.T) { require.Error(t, err) assert.Equal(t, `500 Internal Server Error`, err.Error()) } + +func TestReadyOk(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(200) + w.Write([]byte(`{ + "status": "ready", + "started": "2021-02-24T12:13:37.681813026Z", + "up": "5713h41m50.256128486s" +}`)) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + dur, err := client.Ready(context.Background()) + require.NoError(t, err) + exp := 5713*time.Hour + 41*time.Minute + 50*time.Second + 256128486*time.Nanosecond + assert.Equal(t, exp, dur) +} + +func TestReadyInvalidJson(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(200) + w.Write([]byte(`{ + "status": "ready", +}`)) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + dur, err := client.Ready(context.Background()) + assert.Error(t, err) + assert.Zero(t, dur) +} + +func TestReadyInvalidDuration(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(200) + w.Write([]byte(`{ + "status": "ready", + "started": "2021-02-24T12:13:37.681813026Z", + "up": "1t" +}`)) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + dur, err := client.Ready(context.Background()) + assert.Error(t, err) + assert.Zero(t, dur) +} + +func TestReadyHtml(t *testing.T) { + html := `
` + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "text/html") + w.WriteHeader(200) + w.Write([]byte(html)) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + check, err := client.Ready(context.Background()) + require.Error(t, err) + assert.Zero(t, check) + assert.Equal(t, "error calling Ready: invalid character '<' looking for beginning of value", err.Error()) +} + +func TestReadyFail(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(503) + w.Write([]byte{}) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + dur, err := client.Ready(context.Background()) + require.Error(t, err) + assert.Zero(t, dur) +} + +func TestHealthOk(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(200) + w.Write([]byte(`{"name":"influxdb", "message":"ready for queries and writes", "status":"pass", "checks":[], "version": "2.0.4", "commit": "4e7a59bb9a"}`)) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + check, err := client.Health(context.Background()) + require.NoError(t, err) + require.NotNil(t, check) + assert.Equal(t, "influxdb", check.Name) + assert.Equal(t, "pass", string(check.Status)) + if assert.NotNil(t, check.Message) { + assert.Equal(t, "ready for queries and writes", *check.Message) + } + if assert.NotNil(t, check.Commit) { + assert.Equal(t, "4e7a59bb9a", *check.Commit) + } + if assert.NotNil(t, check.Version) { + assert.Equal(t, "2.0.4", *check.Version) + } + if assert.NotNil(t, check.Checks) { + assert.Len(t, *check.Checks, 0) + } +} + +func TestHealthInvalidJson(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(200) + w.Write([]byte(`{"name":"influxdb",}`)) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + check, err := client.Health(context.Background()) + assert.Error(t, err) + assert.Nil(t, check) +} + +func TestHealthHtml(t *testing.T) { + html := `` + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "text/html") + w.WriteHeader(200) + w.Write([]byte(html)) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + check, err := client.Health(context.Background()) + require.Error(t, err) + assert.Nil(t, check) + assert.Equal(t, "error calling Health: invalid character '<' looking for beginning of value", err.Error()) +} + +func TestHealthFail(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(503) + w.Write([]byte{}) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + check, err := client.Health(context.Background()) + require.Error(t, err) + require.Nil(t, check) +} + +func TestPingOk(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(204) + w.Write([]byte{}) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + err = client.Ping(context.Background()) + require.NoError(t, err) +} + +func TestPingFail(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(503) + w.Write([]byte{}) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + err = client.Ping(context.Background()) + require.Error(t, err) +} + +func TestDeletePointsOk(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(204) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + start, err := time.Parse(time.RFC3339, "2019-08-24T14:15:00Z") + require.NoError(t, err) + stop, err := time.Parse(time.RFC3339, "2019-08-25T14:15:00Z") + require.NoError(t, err) + err = client.DeletePoints(context.Background(), &DeleteParams{ + Org: "my-org", + Bucket: "my-bucket", + Predicate: `_measurement="sensorData"`, + Start: start, + Stop: stop, + }) + require.NoError(t, err) +} + +func TestDeletePointsFail(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(204) + })) + defer ts.Close() + client, err := New(Params{ServerURL: ts.URL, AuthToken: ""}) + require.NoError(t, err) + err = client.DeletePoints(context.Background(), nil) + assert.Error(t, err) + err = client.DeletePoints(context.Background(), &DeleteParams{}) + assert.Error(t, err) + err = client.DeletePoints(context.Background(), &DeleteParams{ + Org: "my-org", + }) + assert.Error(t, err) + err = client.DeletePoints(context.Background(), &DeleteParams{ + Bucket: "my-bucket", + }) + assert.Error(t, err) + err = client.DeletePoints(context.Background(), &DeleteParams{ + Org: "my-org", + Bucket: "my-bucket", + }) + assert.Error(t, err) + err = client.DeletePoints(context.Background(), &DeleteParams{ + Org: "my-org", + Bucket: "my-bucket", + Start: time.Now(), + }) + assert.Error(t, err) + err = client.DeletePoints(context.Background(), &DeleteParams{ + Org: "my-org", + Bucket: "my-bucket", + Stop: time.Now(), + }) + assert.Error(t, err) +} diff --git a/influxclient/e2e_test.go b/influxclient/e2e_test.go index 79f9a6ad..1e14d970 100644 --- a/influxclient/e2e_test.go +++ b/influxclient/e2e_test.go @@ -56,12 +56,15 @@ func newClient(t *testing.T) (*Client, context.Context) { return c, context.Background() } +//lint:ignore ST1003 compatible with generated code syntax func safeId(ID interface{}) string { switch v := ID.(type) { case string: - return fmt.Sprintf("%s", v) + return v case *string: - return fmt.Sprintf("%s", *v) + b := make([]byte, len(*v)) + copy(b, *v) + return string(b) default: panic("unsupported type") } } \ No newline at end of file diff --git a/influxclient/error.go b/influxclient/error.go index ea86a0d2..92fb6b83 100644 --- a/influxclient/error.go +++ b/influxclient/error.go @@ -22,7 +22,6 @@ func NewServerError(message string) *ServerError { func (e *ServerError) Error() string { if e.Code != "" { return fmt.Sprintf("%s: %s", e.Code, e.Message) - } else { - return e.Message } + return e.Message } diff --git a/influxclient/labels.go b/influxclient/labels.go index cfdcb2da..6a0c6cff 100644 --- a/influxclient/labels.go +++ b/influxclient/labels.go @@ -116,7 +116,9 @@ func (a *LabelsAPI) Delete(ctx context.Context, labelID string) error { func (a *LabelsAPI) getLabels(ctx context.Context, filter *Filter) ([]model.Label, error) { params := &model.GetLabelsParams{} if filter != nil { - params.OrgID = &filter.OrgID + if filter.OrgID != "" { + params.OrgID = &filter.OrgID + } } response, err := a.client.GetLabels(ctx, params) if err != nil { diff --git a/influxclient/labels_e2e_test.go b/influxclient/labels_e2e_test.go index d8e57964..72eb9e6a 100644 --- a/influxclient/labels_e2e_test.go +++ b/influxclient/labels_e2e_test.go @@ -123,7 +123,13 @@ func TestLabelsAPI_failing(t *testing.T) { client, ctx := newClient(t) labelsAPI := client.LabelsAPI() - label, err := labelsAPI.Create(ctx, nil) + label, err := labelsAPI.FindOne(ctx, &Filter{ + OrgID: invalidID, + }) + assert.Error(t, err) + assert.Nil(t, label) + + label, err = labelsAPI.Create(ctx, nil) assert.Error(t, err) assert.Nil(t, label) @@ -139,6 +145,13 @@ func TestLabelsAPI_failing(t *testing.T) { assert.Error(t, err) assert.Nil(t, label) + label, err = labelsAPI.Create(ctx, &model.Label{ + Name: &name, + OrgID: &invalidID, + }) + assert.Error(t, err) + assert.Nil(t, label) + label, err = labelsAPI.Update(ctx, nil) assert.Error(t, err) assert.Nil(t, label) @@ -147,15 +160,15 @@ func TestLabelsAPI_failing(t *testing.T) { assert.Error(t, err) assert.Nil(t, label) - err = labelsAPI.Delete(ctx, notExistingID) - require.Error(t, err) - - label, err = labelsAPI.FindOne(ctx, &Filter{ - ID: invalidID, + label, err = labelsAPI.Update(ctx, &model.Label{ + Id: &invalidID, }) assert.Error(t, err) assert.Nil(t, label) + err = labelsAPI.Delete(ctx, notExistingID) + require.Error(t, err) + err = labelsAPI.Delete(ctx, invalidID) assert.Error(t, err) } diff --git a/influxclient/organizations_e2e_test.go b/influxclient/organizations_e2e_test.go index 5e28aeed..4b2942e4 100644 --- a/influxclient/organizations_e2e_test.go +++ b/influxclient/organizations_e2e_test.go @@ -271,6 +271,9 @@ func TestOrganizationAPI_failing(t *testing.T) { err = orgsAPI.AddMember(ctx, *org.Id, notExistingID) assert.Error(t, err) + err = orgsAPI.AddMember(ctx, notInitializedID, notExistingID) + assert.Error(t, err) + err = orgsAPI.AddMember(ctx, *org.Id, notInitializedID) assert.Error(t, err) diff --git a/influxclient/query.go b/influxclient/query.go index 39403806..f3ffb15e 100644 --- a/influxclient/query.go +++ b/influxclient/query.go @@ -93,9 +93,8 @@ func (r *QueryResultReader) errorSection() error { if !r.NextRow() { if r.err != nil { return r.err - } else { - return errors.New("no row found in error section") } + return errors.New("no row found in error section") } row := r.Row() if row[0] == "" { diff --git a/influxclient/tasks_e2e_test.go b/influxclient/tasks_e2e_test.go index d08368aa..a05410d4 100644 --- a/influxclient/tasks_e2e_test.go +++ b/influxclient/tasks_e2e_test.go @@ -711,6 +711,12 @@ func TestTasksAPI_Failures(t *testing.T) { err = tasksAPI.RemoveMember(ctx, notExistingID, invalidID) assert.Error(t, err) + err = tasksAPI.RemoveMember(ctx, notInitializedID, notExistingID) + assert.Error(t, err) + + err = tasksAPI.RemoveMember(ctx, notExistingID, notInitializedID) + assert.Error(t, err) + _, err = tasksAPI.Owners(ctx, invalidID) assert.Error(t, err) diff --git a/influxclient/users_e2e_test.go b/influxclient/users_e2e_test.go index c5c7961e..e48c668a 100644 --- a/influxclient/users_e2e_test.go +++ b/influxclient/users_e2e_test.go @@ -7,7 +7,6 @@ package influxclient_test import ( - "fmt" "testing" . "github.com/influxdata/influxdb-client-go/influxclient" @@ -31,7 +30,7 @@ func TestUsersAPI(t *testing.T) { }) require.NoError(t, err) require.NotNil(t, user) - defer usersAPI.Delete(ctx, fmt.Sprintf("%s", *user.Id)) + defer usersAPI.Delete(ctx, safeId(user.Id)) // try to create duplicate user user2, err := usersAPI.Create(ctx, &model.User{ @@ -78,6 +77,15 @@ func TestUsersAPI(t *testing.T) { require.NoError(t, err) require.NotNil(t, user) + // find multiple + users, err = usersAPI.Find(ctx, &Filter{ + Offset: 1, + Limit: 100, + }) + require.NoError(t, err) + require.NotNil(t, users) + require.Equal(t, 1, len(users)) + // update password err = usersAPI.SetPassword(ctx, *user.Id, "my-password2") require.NoError(t, err) diff --git a/scripts/influxdb-restart.sh b/scripts/influxdb-restart.sh new file mode 100755 index 00000000..9c8620d3 --- /dev/null +++ b/scripts/influxdb-restart.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# +# The MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +set -e + +DEFAULT_INFLUXDB_VERSION="1.8.3" +INFLUXDB_VERSION="${INFLUXDB_VERSION:-$DEFAULT_INFLUXDB_VERSION}" +INFLUXDB_IMAGE=influxdb:${INFLUXDB_VERSION}-alpine + +DEFAULT_INFLUXDB_V2_VERSION="latest" +INFLUXDB_V2_VERSION="${INFLUXDB_V2_VERSION:-$DEFAULT_INFLUXDB_V2_VERSION}" +INFLUXDB_V2_IMAGE=influxdb:${INFLUXDB_V2_VERSION} + +SCRIPT_PATH="$( cd "$(dirname "$0")" ; pwd -P )" + +docker kill influxdb || true +docker rm influxdb || true +docker kill influxdb_v2 || true +docker rm influxdb_v2 || true +docker kill influxdb_v2_onboarding || true +docker rm influxdb_v2_onboarding || true +docker network rm influx_network || true +docker network create -d bridge influx_network --subnet 192.168.0.0/24 --gateway 192.168.0.1 + + +echo +echo "Restarting InfluxDB [${INFLUXDB_IMAGE}] ..." +echo + +# +# InfluxDB 1.8 +# + +docker pull ${INFLUXDB_IMAGE} || true +docker run \ + --detach \ + --name influxdb \ + --network influx_network \ + --publish 8087:8086 \ + --volume ${SCRIPT_PATH}/influxdb.conf:/etc/influxdb/influxdb.conf \ + ${INFLUXDB_IMAGE} + +echo "Wait to start InfluxDB" +wget -S --spider --tries=20 --retry-connrefused --waitretry=5 http://localhost:8087/ping +echo +echo "Post with create dabase" +echo +curl -X POST localhost:8087/query --data-urlencode "q=create database mydb" +# +# InfluxDB 2.0 +# +echo +echo "Restarting InfluxDB 2.0 [${INFLUXDB_V2_IMAGE}] ... " +echo + +docker pull ${INFLUXDB_V2_IMAGE} || true +docker run \ + --detach \ + --name influxdb_v2 \ + --network influx_network \ + --publish 8086:8086 \ + ${INFLUXDB_V2_IMAGE} + +echo "Wait to start InfluxDB 2.0" +wget -S --spider --tries=20 --retry-connrefused --waitretry=5 http://localhost:8086/metrics + +echo +echo "Post onBoarding request, to setup initial user (my-user@my-password), org (my-org) and bucketSetup (my-bucket)" +echo +curl -i -X POST http://localhost:8086/api/v2/setup -H 'accept: application/json' \ + -d '{ + "username": "my-user", + "password": "my-password", + "org": "my-org", + "bucket": "my-bucket", + "token": "my-token" + }' + +# +# InfluxDB 2.0 +# +echo +echo "Restarting InfluxDB 2.0 for onboarding test... " +echo + +docker run \ + --detach \ + --name influxdb_v2_onboarding \ + --network influx_network \ + --publish 8089:8086 \ + ${INFLUXDB_V2_IMAGE} + diff --git a/scripts/influxdb.conf b/scripts/influxdb.conf new file mode 100755 index 00000000..c80a3d94 --- /dev/null +++ b/scripts/influxdb.conf @@ -0,0 +1,43 @@ +# +# The MIT License +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +[meta] + dir = "/var/lib/influxdb/meta" + +[data] + dir = "/var/lib/influxdb/data" + engine = "tsm1" + wal-dir = "/var/lib/influxdb/wal" + + +[http] + flux-enabled = true + +# These next lines control how batching works. You should have this enabled +# otherwise you could get dropped metrics or poor performance. Batching +# will buffer points in memory if you have many coming in. + +batch-size = 1000 # will flush if this many points get buffered +batch-pending = 5 # number of batches that may be pending in memory +batch-timeout = "1s" # will flush at least this often even if we haven't hit buffer limit +read-buffer = 0 # UDP Read buffer size, 0 means OS default. UDP listener will fail if set above OS max.