From ec113b3356b8b1b90c6811c8dd47ce9755e87fa4 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Tue, 14 Jun 2022 10:50:25 +0200 Subject: [PATCH 01/18] fix(ui): detect unsupported value properly --- ui/src/kapacitor/components/RuleGraphDygraph.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/kapacitor/components/RuleGraphDygraph.tsx b/ui/src/kapacitor/components/RuleGraphDygraph.tsx index f9fc58f9b..240f360c5 100644 --- a/ui/src/kapacitor/components/RuleGraphDygraph.tsx +++ b/ui/src/kapacitor/components/RuleGraphDygraph.tsx @@ -64,7 +64,7 @@ class RuleGraphDygraph extends Component { if (!timeSeriesToDygraphResult) { return null } - if (timeSeriesToDygraphResult.unsupportedValue) { + if (timeSeriesToDygraphResult.unsupportedValue !== undefined) { console.error( 'Unsupported y-axis value, cannot display data', timeSeriesToDygraphResult From a2f0a8223bf4cdaf62f204dc9388ca4fb5a4c208 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Tue, 14 Jun 2022 10:55:27 +0200 Subject: [PATCH 02/18] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 739d06145..beb415182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ 1. [#5913](https://github.com/influxdata/chronograf/pull/5913): Improve InfluxDB Enterprise detection. 1. [#5917](https://github.com/influxdata/chronograf/pull/5917): Improve InfluxDB Enterprise user creation process. 1. [#5917](https://github.com/influxdata/chronograf/pull/5917): Avoid stale reads in communication with InfluxDB Enterprise meta nodes. +1. [#5938](https://github.com/influxdata/chronograf/pull/5938): Properly detect unsupported values in Alert Rule builder. ### Other From c6a384f0b001170175314db88acb7414946fc2f0 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Tue, 14 Jun 2022 13:45:29 +0200 Subject: [PATCH 03/18] feat(server): add AppendPath utility function --- util/path.go | 21 +++++++++++++++ util/path_test.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 util/path.go create mode 100644 util/path_test.go diff --git a/util/path.go b/util/path.go new file mode 100644 index 000000000..27817ad31 --- /dev/null +++ b/util/path.go @@ -0,0 +1,21 @@ +package util + +import ( + "net/url" +) + +// AppendPath appends path to the supplied URL and returns a new URL instance. +func AppendPath(url *url.URL, path string) *url.URL { + retVal := *url + if len(path) == 0 { + return &retVal + } + if path[0] != '/' { + path = "/" + path + } + if len(retVal.Path) > 0 && retVal.Path[len(retVal.Path)-1] == '/' { + retVal.Path = retVal.Path[0 : len(retVal.Path)-1] + } + retVal.Path += path + return &retVal +} diff --git a/util/path_test.go b/util/path_test.go new file mode 100644 index 000000000..d6e5d69e7 --- /dev/null +++ b/util/path_test.go @@ -0,0 +1,69 @@ +package util_test + +import ( + "net/url" + "testing" + + "github.com/influxdata/chronograf/util" +) + +func Test_AppendPath(t *testing.T) { + tests := []struct { + url string + path string + expected string + }{ + { + url: "http://localhost:8086?t=1#asdf", + path: "", + expected: "http://localhost:8086?t=1#asdf", + }, + { + url: "http://localhost:8086?t=1#asdf", + path: "a", + expected: "http://localhost:8086/a?t=1#asdf", + }, + { + url: "http://localhost:8086/?t=1#asdf", + path: "", + expected: "http://localhost:8086/?t=1#asdf", + }, + { + url: "http://localhost:8086/a?t=1#asdf", + path: "", + expected: "http://localhost:8086/a?t=1#asdf", + }, + { + url: "http://localhost:8086/a?t=1#asdf", + path: "b", + expected: "http://localhost:8086/a/b?t=1#asdf", + }, + { + url: "http://localhost:8086/a?t=1#asdf", + path: "/b", + expected: "http://localhost:8086/a/b?t=1#asdf", + }, + { + url: "http://localhost:8086/a/?t=1#asdf", + path: "b", + expected: "http://localhost:8086/a/b?t=1#asdf", + }, + { + url: "http://localhost:8086/a/?t=1#asdf", + path: "/b", + expected: "http://localhost:8086/a/b?t=1#asdf", + }, + } + + for _, test := range tests { + inURL, _ := url.Parse(test.url) + outURL := util.AppendPath(inURL, test.path) + if inURL == outURL { + t.Errorf("AppendPath(\"%v\",\"%v\") does not return a new URL instance", inURL, test.path) + } + out := outURL.String() + if out != test.expected { + t.Errorf("AppendPath(\"%v\",\"%v\") != \"%v\"", inURL, test.path, test.expected) + } + } +} From 8c927f5893c948dde516891c305bca466c8fc3c6 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Tue, 14 Jun 2022 13:48:27 +0200 Subject: [PATCH 04/18] feat(server): keep context path in communication with InfluxDB flux query endpoint --- flux/client.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/flux/client.go b/flux/client.go index 3021af2c8..c9a0873a2 100644 --- a/flux/client.go +++ b/flux/client.go @@ -54,8 +54,7 @@ func (c *Client) pingTimeout(ctx context.Context) error { // FluxEnabled returns true if the server has flux querying enabled. func (c *Client) FluxEnabled() (bool, error) { - url := c.URL - url.Path = "/api/v2/query" + url := util.AppendPath(c.URL, "/api/v2/query") req, err := http.NewRequest("POST", url.String(), nil) if err != nil { @@ -86,7 +85,7 @@ func (c *Client) FluxEnabled() (bool, error) { } func (c *Client) ping(u *url.URL) error { - u.Path = "ping" + u = util.AppendPath(u, "/ping") req, err := http.NewRequest("GET", u.String(), nil) if err != nil { From 1969aef4b4692b249d4a7fac325ffbaf03a0a234 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Tue, 14 Jun 2022 13:50:21 +0200 Subject: [PATCH 05/18] feat(server): keep context path in communication with InfluxDB write endpoint --- server/influx.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/influx.go b/server/influx.go index 090458640..4ae2ee28b 100644 --- a/server/influx.go +++ b/server/influx.go @@ -16,6 +16,7 @@ import ( "github.com/influxdata/chronograf" uuid "github.com/influxdata/chronograf/id" "github.com/influxdata/chronograf/influx" + "github.com/influxdata/chronograf/util" ) // ValidInfluxRequest checks if queries specify a command. @@ -124,13 +125,13 @@ func (s *Service) Write(w http.ResponseWriter, r *http.Request) { version := query.Get("v") query.Del("v") if strings.HasPrefix(version, "2") { - u.Path = "/api/v2/write" + u = util.AppendPath(u, "/api/v2/write") // v2 organization name is stored in username (org does not matter against v1) query.Set("org", src.Username) query.Set("bucket", query.Get("db")) query.Del("db") } else { - u.Path = "/write" + u = util.AppendPath(u, "/write") } u.RawQuery = query.Encode() From 614424fc6fe725dc5a4bb5b233dfc4c91db48a04 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Tue, 14 Jun 2022 13:55:28 +0200 Subject: [PATCH 06/18] feat(server): keep context path in communication with InfluxDB v1 API endpoint --- influx/influx.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/influx/influx.go b/influx/influx.go index ebfb94bbd..ea3fd520d 100644 --- a/influx/influx.go +++ b/influx/influx.go @@ -65,7 +65,8 @@ func (r *responseType) Error() string { } func (c *Client) query(u *url.URL, q chronograf.Query) (chronograf.Response, error) { - u.Path = "query" + u = util.AppendPath(u, "/query") + req, err := http.NewRequest("POST", u.String(), nil) if err != nil { return nil, err @@ -183,7 +184,7 @@ func (c *Client) validateAuthFlux(ctx context.Context, src *chronograf.Source) e if err != nil { return err } - u.Path = "api/v2/query" + u = util.AppendPath(u, "/api/v2/query") command := "buckets()" req, err := http.NewRequest("POST", u.String(), strings.NewReader(command)) if err != nil { @@ -297,7 +298,7 @@ type pingResult struct { } func (c *Client) ping(u *url.URL) (string, string, error) { - u.Path = "ping" + u = util.AppendPath(u, "/ping") req, err := http.NewRequest("GET", u.String(), nil) if err != nil { @@ -392,7 +393,7 @@ func (c *Client) writePoint(ctx context.Context, point *chronograf.Point) error } func (c *Client) write(ctx context.Context, u *url.URL, db, rp, lp string) error { - u.Path = "write" + u = util.AppendPath(u, "/write") req, err := http.NewRequest("POST", u.String(), strings.NewReader(lp)) if err != nil { return err From b8717ff95530656406790d7160f2bf8a82863049 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Wed, 15 Jun 2022 08:42:08 +0200 Subject: [PATCH 07/18] chore(server): removed unused code --- flux/client.go | 63 -------------------------------------------------- 1 file changed, 63 deletions(-) diff --git a/flux/client.go b/flux/client.go index c9a0873a2..dfa392634 100644 --- a/flux/client.go +++ b/flux/client.go @@ -1,15 +1,11 @@ package flux import ( - "context" - "errors" - "io/ioutil" "net/http" "net/url" "strings" "time" - "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/util" ) @@ -26,32 +22,6 @@ type Client struct { Timeout time.Duration } -// Ping checks the connection of a Flux. -func (c *Client) Ping(ctx context.Context) error { - t := 2 * time.Second - if c.Timeout > 0 { - t = c.Timeout - } - ctx, cancel := context.WithTimeout(ctx, t) - defer cancel() - err := c.pingTimeout(ctx) - return err -} - -func (c *Client) pingTimeout(ctx context.Context) error { - resps := make(chan (error)) - go func() { - resps <- c.ping(c.URL) - }() - - select { - case resp := <-resps: - return resp - case <-ctx.Done(): - return chronograf.ErrUpstreamTimeout - } -} - // FluxEnabled returns true if the server has flux querying enabled. func (c *Client) FluxEnabled() (bool, error) { url := util.AppendPath(c.URL, "/api/v2/query") @@ -83,36 +53,3 @@ func (c *Client) FluxEnabled() (bool, error) { // {"code":"unauthorized","message":"unauthorized access"} is received return strings.HasPrefix(contentType, "application/json"), nil } - -func (c *Client) ping(u *url.URL) error { - u = util.AppendPath(u, "/ping") - - req, err := http.NewRequest("GET", u.String(), nil) - if err != nil { - return err - } - - hc := &http.Client{} - if c.InsecureSkipVerify { - hc.Transport = skipVerifyTransport - } else { - hc.Transport = defaultTransport - } - - resp, err := hc.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - - if resp.StatusCode != http.StatusNoContent { - return errors.New(string(body)) - } - - return nil -} From 2dd0b82fbca75ef503ff5f93d4af4281bb8fc4f6 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Wed, 15 Jun 2022 09:25:02 +0200 Subject: [PATCH 08/18] feat(flux): add test for flux client's FluxEnabled --- flux/client_test.go | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 flux/client_test.go diff --git a/flux/client_test.go b/flux/client_test.go new file mode 100644 index 000000000..44a61dd93 --- /dev/null +++ b/flux/client_test.go @@ -0,0 +1,56 @@ +package flux_test + +import ( + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" + + "github.com/influxdata/chronograf/flux" +) + +// NewClient initializes an HTTP Client for InfluxDB. +func NewClient(urlStr string) *flux.Client { + u, _ := url.Parse(urlStr) + return &flux.Client{ + URL: u, + Timeout: 500 * time.Millisecond, + } +} + +func Test_FluxEnabled(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + path := r.URL.Path + if !strings.HasSuffix(path, "/api/v2/query") { + t.Error("Expected the path to contain `/api/v2/query` but was", path) + } + if strings.HasPrefix(path, "/enabled_v1") { + rw.Header().Add("Content-Type", "application/json") + rw.WriteHeader(http.StatusBadRequest) + rw.Write([]byte(`{}`)) + return + } + if strings.HasPrefix(path, "/enabled_v2") { + rw.Header().Add("Content-Type", "application/json") + rw.WriteHeader(http.StatusUnauthorized) + rw.Write([]byte(`{"code":"unauthorized","message":"unauthorized access"}`)) + return + } + rw.Header().Add("Content-Type", "text/plain") + rw.WriteHeader(http.StatusForbidden) + rw.Write([]byte(`Flux query service disabled.`)) + })) + defer ts.Close() + + if enabled, _ := NewClient(ts.URL).FluxEnabled(); enabled { + t.Errorf("Client.FluxEnabled() expected false value") + } + if enabled, _ := NewClient(ts.URL + "/enabled_v1").FluxEnabled(); !enabled { + t.Errorf("Client.FluxEnabled() expected true value") + } + if enabled, _ := NewClient(ts.URL + "/enabled_v2").FluxEnabled(); !enabled { + t.Errorf("Client.FluxEnabled() expected true value") + } +} From fabc3065c439579ee4b01ab0cf563b860265e3e4 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Wed, 15 Jun 2022 10:52:16 +0200 Subject: [PATCH 09/18] feat(server): add tests for influxdb connection validation --- influx/influx_test.go | 98 +++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/influx/influx_test.go b/influx/influx_test.go index dc04caa1a..49556a5fd 100644 --- a/influx/influx_test.go +++ b/influx/influx_test.go @@ -539,14 +539,11 @@ func TestClient_write(t *testing.T) { func Test_Influx_ValidateAuth_V1(t *testing.T) { t.Parallel() - called := false + calledPath := "" ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusUnauthorized) rw.Write([]byte(`{"error":"v1authfailed"}`)) - called = true - if path := r.URL.Path; path != "/query" { - t.Error("Expected the path to contain `/query` but was: ", path) - } + calledPath = r.URL.Path expectedAuth := "Basic " + base64.StdEncoding.EncodeToString(([]byte)("my-user:my-pwd")) if auth := r.Header.Get("Authorization"); auth != expectedAuth { t.Errorf("Expected Authorization '%v' but was: %v", expectedAuth, auth) @@ -554,66 +551,69 @@ func Test_Influx_ValidateAuth_V1(t *testing.T) { })) defer ts.Close() - client, err := NewClient(ts.URL, log.New(log.DebugLevel)) - if err != nil { - t.Fatal("Unexpected error initializing client: err:", err) - } + for _, urlContext := range []string{"", "/ctx"} { + client, err := NewClient(ts.URL+urlContext, log.New(log.DebugLevel)) + if err != nil { + t.Fatal("Unexpected error initializing client: err:", err) + } + source := &chronograf.Source{ + URL: ts.URL + urlContext, + Username: "my-user", + Password: "my-pwd", + } - source := &chronograf.Source{ - URL: ts.URL, - Username: "my-user", - Password: "my-pwd", - } - - client.Connect(context.Background(), source) - err = client.ValidateAuth(context.Background(), &chronograf.Source{}) - if err == nil { - t.Fatal("Expected error but nil") - } - if !strings.Contains(err.Error(), "v1authfailed") { - t.Errorf("Expected client error '%v' to contain server-sent error message", err) - } - if called == false { - t.Error("Expected http request to InfluxDB but there was none") + client.Connect(context.Background(), source) + err = client.ValidateAuth(context.Background(), &chronograf.Source{}) + if err == nil { + t.Fatal("Expected error but nil") + } + if !strings.Contains(err.Error(), "v1authfailed") { + t.Errorf("Expected client error '%v' to contain server-sent error message", err) + } + if calledPath != urlContext+"/query" { + t.Errorf("Path received: %v, want: %v ", calledPath, urlContext+"/query") + } } } func Test_Influx_ValidateAuth_V2(t *testing.T) { t.Parallel() - called := false + calledPath := "" ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusUnauthorized) rw.Write([]byte(`{"message":"v2authfailed"}`)) - called = true + calledPath = r.URL.Path if auth := r.Header.Get("Authorization"); auth != "Token my-token" { t.Error("Expected Authorization 'Token my-token' but was: ", auth) } - if path := r.URL.Path; path != "/api/v2/query" { + if path := r.URL.Path; !strings.HasSuffix(path, "/api/v2/query") { t.Error("Expected the path to contain `api/v2/query` but was: ", path) } })) defer ts.Close() + for _, urlContext := range []string{"", "/ctx"} { + calledPath = "" + client, err := NewClient(ts.URL+urlContext, log.New(log.DebugLevel)) + if err != nil { + t.Fatal("Unexpected error initializing client: err:", err) + } + source := &chronograf.Source{ + URL: ts.URL + urlContext, + Type: chronograf.InfluxDBv2, + Username: "my-org", + Password: "my-token", + } - client, err := NewClient(ts.URL, log.New(log.DebugLevel)) - if err != nil { - t.Fatal("Unexpected error initializing client: err:", err) - } - source := &chronograf.Source{ - URL: ts.URL, - Type: chronograf.InfluxDBv2, - Username: "my-org", - Password: "my-token", - } - - client.Connect(context.Background(), source) - err = client.ValidateAuth(context.Background(), source) - if err == nil { - t.Fatal("Expected error but nil") - } - if !strings.Contains(err.Error(), "v2authfailed") { - t.Errorf("Expected client error '%v' to contain server-sent error message", err) - } - if called == false { - t.Error("Expected http request to InfluxDB but there was none") + client.Connect(context.Background(), source) + err = client.ValidateAuth(context.Background(), source) + if err == nil { + t.Fatal("Expected error but nil") + } + if !strings.Contains(err.Error(), "v2authfailed") { + t.Errorf("Expected client error '%v' to contain server-sent error message", err) + } + if calledPath != urlContext+"/api/v2/query" { + t.Errorf("Path received: %v, want: %v ", calledPath, urlContext+"/api/v2/query") + } } } From 9fc48da1429eae34c343e3f921d53660d7ecca4a Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Wed, 15 Jun 2022 11:14:13 +0200 Subject: [PATCH 10/18] feat(server): add tests for InfluxDB version detection --- influx/influx_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/influx/influx_test.go b/influx/influx_test.go index 49556a5fd..1866646fa 100644 --- a/influx/influx_test.go +++ b/influx/influx_test.go @@ -617,3 +617,58 @@ func Test_Influx_ValidateAuth_V2(t *testing.T) { } } } + +func Test_Influx_Version(t *testing.T) { + t.Parallel() + calledPath := "" + serverVersion := "" + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.Header().Add("X-Influxdb-Version", serverVersion) + rw.WriteHeader(http.StatusNoContent) + calledPath = r.URL.Path + + })) + defer ts.Close() + for _, urlContext := range []string{"", "/ctx"} { + calledPath = "" + client, err := NewClient(ts.URL+urlContext, log.New(log.DebugLevel)) + if err != nil { + t.Fatal("Unexpected error initializing client: err:", err) + } + source := &chronograf.Source{ + URL: ts.URL + urlContext, + Type: chronograf.InfluxDBv2, + Username: "my-org", + Password: "my-token", + } + + client.Connect(context.Background(), source) + + versions := []struct { + server string + expected string + }{ + { + server: "1.8.3", + expected: "1.8.3", + }, + { + server: "v2.2.0", + expected: "2.2.0", + }, + } + for _, testPair := range versions { + serverVersion = testPair.server + version, err := client.Version(context.Background()) + if err != nil { + t.Fatalf("No error expected, but %v", err) + } + if version != testPair.expected { + t.Errorf("Version received: %v, want: %v ", version, testPair.expected) + } + if calledPath != urlContext+"/ping" { + t.Errorf("Path received: %v, want: %v ", calledPath, urlContext+"/ping") + } + } + } +} From cd78ba1680c2136bd693ca6e1993489530a883d7 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Wed, 15 Jun 2022 11:52:59 +0200 Subject: [PATCH 11/18] feat(server): add tests for InfluxDB write --- influx/influx_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/influx/influx_test.go b/influx/influx_test.go index 1866646fa..b4e736290 100644 --- a/influx/influx_test.go +++ b/influx/influx_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "fmt" + "io/ioutil" "net/http" "net/http/httptest" "net/url" @@ -661,7 +662,7 @@ func Test_Influx_Version(t *testing.T) { serverVersion = testPair.server version, err := client.Version(context.Background()) if err != nil { - t.Fatalf("No error expected, but %v", err) + t.Fatalf("No error expected, but received: %v", err) } if version != testPair.expected { t.Errorf("Version received: %v, want: %v ", version, testPair.expected) @@ -672,3 +673,52 @@ func Test_Influx_Version(t *testing.T) { } } } + +func Test_Write(t *testing.T) { + t.Parallel() + calledPath := "" + data := "" + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + calledPath = r.URL.Path + content, _ := ioutil.ReadAll(r.Body) + data = string(content) + rw.WriteHeader(http.StatusNoContent) + })) + defer ts.Close() + for _, urlContext := range []string{"", "/ctx"} { + calledPath = "" + client, err := NewClient(ts.URL+urlContext, log.New(log.DebugLevel)) + if err != nil { + t.Fatal("Unexpected error initializing client: err:", err) + } + source := &chronograf.Source{ + URL: ts.URL + urlContext, + Type: chronograf.InfluxDBv2, + Username: "my-org", + Password: "my-token", + } + + client.Connect(context.Background(), source) + + err = client.Write(context.Background(), []chronograf.Point{ + { + Database: "mydb", + RetentionPolicy: "default", + Measurement: "temperature", + Fields: map[string]interface{}{ + "v": true, + }, + }, + }) + if err != nil { + t.Fatalf("No error expected, but received: %v", err) + } + expectedLine := "temperature v=true" + if data != expectedLine { + t.Errorf("Data received: %v, want: %v ", data, expectedLine) + } + if calledPath != urlContext+"/write" { + t.Errorf("Path received: %v, want: %v ", calledPath, urlContext+"/write") + } + } +} From f2299043b33627e76a32f749041ca9c554df81e5 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Wed, 15 Jun 2022 12:02:01 +0200 Subject: [PATCH 12/18] feat(server): add tests for InfluxDB query --- influx/influx_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/influx/influx_test.go b/influx/influx_test.go index b4e736290..b64a5dd68 100644 --- a/influx/influx_test.go +++ b/influx/influx_test.go @@ -722,3 +722,42 @@ func Test_Write(t *testing.T) { } } } + +func Test_Query(t *testing.T) { + t.Parallel() + calledPath := "" + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + calledPath = r.URL.Path + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(`{"message":"hi"}`)) + })) + defer ts.Close() + + for _, urlContext := range []string{"", "/ctx"} { + calledPath = "" + client, err := NewClient(ts.URL+urlContext, log.New(log.DebugLevel)) + if err != nil { + t.Fatal("Unexpected error initializing client: err:", err) + } + source := &chronograf.Source{ + URL: ts.URL + urlContext, + Type: chronograf.InfluxDBv2, + Username: "my-org", + Password: "my-token", + } + + client.Connect(context.Background(), source) + + _, err = client.Query(context.Background(), chronograf.Query{ + DB: "mydb", + RP: "default", + Command: "show databases", + }) + if err != nil { + t.Fatalf("No error expected, but received: %v", err) + } + if calledPath != urlContext+"/query" { + t.Errorf("Path received: %v, want: %v ", calledPath, urlContext+"/query") + } + } +} From 799e49f1bc4d8930da50d4181552fd9bdb7d2b7b Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Wed, 15 Jun 2022 12:32:01 +0200 Subject: [PATCH 13/18] feat(server): add tests for InfluxDB write proxy --- server/influx_test.go | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/server/influx_test.go b/server/influx_test.go index 06ebfebbc..3cd88b3a5 100644 --- a/server/influx_test.go +++ b/server/influx_test.go @@ -216,3 +216,69 @@ func TestService_Influx_UseCommand(t *testing.T) { }) } } + +func TestService_Influx_Write(t *testing.T) { + calledPath := "" + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + calledPath = r.URL.Path + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(`{"message":"hi"}`)) + })) + defer ts.Close() + + testPairs := []struct { + version string + ctx string + path string + }{ + {version: "1.8.3", ctx: "", path: "/write"}, + {version: "1.8.3", ctx: "/ctx", path: "/ctx/write"}, + {version: "2.2.0", ctx: "", path: "/api/v2/write"}, + {version: "2.2.0", ctx: "/ctx", path: "/ctx/api/v2/write"}, + } + + for _, testPair := range testPairs { + calledPath = "" + w := httptest.NewRecorder() + r := httptest.NewRequest( + "POST", + "http://any.url?v="+testPair.version, + ioutil.NopCloser( + bytes.NewReader([]byte( + `temperature v=1.0`, + )), + ), + ) + r = r.WithContext(httprouter.WithParams( + context.Background(), + httprouter.Params{ + { + Key: "id", + Value: "1", + }, + }, + )) + + h := &Service{ + Store: &mocks.Store{ + SourcesStore: &mocks.SourcesStore{ + GetF: func(ctx context.Context, ID int) (chronograf.Source, error) { + return chronograf.Source{ + ID: 1337, + URL: ts.URL + testPair.ctx, + }, nil + }, + }, + }, + Logger: log.New(log.ErrorLevel), + } + h.Write(w, r) + + resp := w.Result() + ioutil.ReadAll(resp.Body) + + if calledPath != testPair.path { + t.Errorf("Path received: %v, want: %v ", calledPath, testPair.path) + } + } +} From 85db81a6c911b554b8aa3967c50964a72eecb098 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 16 Jun 2022 08:12:43 +0200 Subject: [PATCH 14/18] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 739d06145..84a801859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ 1. [#5926](https://github.com/influxdata/chronograf/pull/5926): Improve InfluxDB role creation. 1. [#5927](https://github.com/influxdata/chronograf/pull/5927): Show effective permissions on Users page. 1. [#5929](https://github.com/influxdata/chronograf/pull/5926): Add refresh button to InfluxDB Users/Roles/Databases page. +1. [#5940](https://github.com/influxdata/chronograf/pull/5940): Support InfluxDB behind proxy under subpath. ### Bug Fixes From b60f80cac1bd5d820fef9da8b4f33d735d75be54 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 16 Jun 2022 08:53:07 +0200 Subject: [PATCH 15/18] fix(ui/admin): disable influxdb admin pages on load error --- ui/src/admin/actions/influxdb.js | 36 ++++++------------- .../influxdb/AdminInfluxDBScopedPage.tsx | 13 +++++-- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/ui/src/admin/actions/influxdb.js b/ui/src/admin/actions/influxdb.js index 4f58406aa..902766659 100644 --- a/ui/src/admin/actions/influxdb.js +++ b/ui/src/admin/actions/influxdb.js @@ -252,41 +252,25 @@ export const editRetentionPolicyFailed = ( // async actions export const loadUsersAsync = url => async dispatch => { - try { - const {data} = await getUsersAJAX(url) - dispatch(loadUsers(data)) - } catch (error) { - dispatch(errorThrown(error)) - } + const {data} = await getUsersAJAX(url) + dispatch(loadUsers(data)) } export const loadRolesAsync = url => async dispatch => { - try { - const {data} = await getRolesAJAX(url) - dispatch(loadRoles(data)) - } catch (error) { - dispatch(errorThrown(error)) - } + const {data} = await getRolesAJAX(url) + dispatch(loadRoles(data)) } export const loadPermissionsAsync = url => async dispatch => { - try { - const {data} = await getPermissionsAJAX(url) - dispatch(loadPermissions(data)) - } catch (error) { - dispatch(errorThrown(error)) - } + const {data} = await getPermissionsAJAX(url) + dispatch(loadPermissions(data)) } export const loadDBsAndRPsAsync = url => async dispatch => { - try { - const { - data: {databases}, - } = await getDbsAndRpsAJAX(url) - dispatch(loadDatabases(_.sortBy(databases, ({name}) => name.toLowerCase()))) - } catch (error) { - dispatch(errorThrown(error)) - } + const { + data: {databases}, + } = await getDbsAndRpsAJAX(url) + dispatch(loadDatabases(_.sortBy(databases, ({name}) => name.toLowerCase()))) } export const createUserAsync = (url, user) => async dispatch => { diff --git a/ui/src/admin/containers/influxdb/AdminInfluxDBScopedPage.tsx b/ui/src/admin/containers/influxdb/AdminInfluxDBScopedPage.tsx index 2bdc0108f..d50ba3b66 100644 --- a/ui/src/admin/containers/influxdb/AdminInfluxDBScopedPage.tsx +++ b/ui/src/admin/containers/influxdb/AdminInfluxDBScopedPage.tsx @@ -111,8 +111,17 @@ export class AdminInfluxDBScopedPage extends PureComponent { } } this.setState({loading: RemoteDataState.Done}) - } catch (error) { - console.error(error) + } catch (e) { + console.error(e) + // extract error message for the UI + let error = e + if (error.message) { + error = error.message + } else if (error.data?.message) { + error = error.data?.message + } else if (error.statusText) { + error = error.statusText + } this.setState({ loading: RemoteDataState.Error, error, From 8bf7baf1ab13c550a9cc0ee44da9171fe7fa8c88 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 16 Jun 2022 20:12:08 +0200 Subject: [PATCH 16/18] fix(ui): simplify no data message --- ui/src/admin/components/EmptyRow.tsx | 23 ------ .../admin/components/influxdb/NoEntities.tsx | 17 +++++ .../admin/containers/influxdb/RolesPage.tsx | 68 +++++++++--------- .../admin/containers/influxdb/UsersPage.tsx | 72 +++++++++---------- ui/src/style/components/tables.scss | 5 ++ 5 files changed, 88 insertions(+), 97 deletions(-) delete mode 100644 ui/src/admin/components/EmptyRow.tsx create mode 100644 ui/src/admin/components/influxdb/NoEntities.tsx diff --git a/ui/src/admin/components/EmptyRow.tsx b/ui/src/admin/components/EmptyRow.tsx deleted file mode 100644 index 7c8faddff..000000000 --- a/ui/src/admin/components/EmptyRow.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, {FunctionComponent} from 'react' - -interface Props { - entities: string - colSpan?: number - filtered?: boolean -} -const EmptyRow: FunctionComponent = ({entities, colSpan, filtered}) => ( - - - {filtered ? ( -

No Matching {entities}

- ) : ( -

- You don't have any {entities},
- why not create one? -

- )} - - -) - -export default EmptyRow diff --git a/ui/src/admin/components/influxdb/NoEntities.tsx b/ui/src/admin/components/influxdb/NoEntities.tsx new file mode 100644 index 000000000..81093f436 --- /dev/null +++ b/ui/src/admin/components/influxdb/NoEntities.tsx @@ -0,0 +1,17 @@ +import React, {FunctionComponent} from 'react' + +interface Props { + entities: string + filtered?: boolean +} +const NoEntities: FunctionComponent = ({entities, filtered}) => + filtered ? ( +

No Matching {entities} Found

+ ) : ( +

+ You don't have any {entities},
+ why not create one? +

+ ) + +export default NoEntities diff --git a/ui/src/admin/containers/influxdb/RolesPage.tsx b/ui/src/admin/containers/influxdb/RolesPage.tsx index eb462ddcc..a45b0da0c 100644 --- a/ui/src/admin/containers/influxdb/RolesPage.tsx +++ b/ui/src/admin/containers/influxdb/RolesPage.tsx @@ -18,7 +18,7 @@ import AdminInfluxDBTabbedPage, { isConnectedToLDAP, } from './AdminInfluxDBTabbedPage' import FancyScrollbar from 'src/shared/components/FancyScrollbar' -import EmptyRow from 'src/admin/components/EmptyRow' +import NoEntities from 'src/admin/components/influxdb/NoEntities' import RoleRow from 'src/admin/components/RoleRow' import {useCallback} from 'react' import allOrParticularSelection from '../../util/allOrParticularSelection' @@ -207,30 +207,30 @@ const RolesPage = ({
- - - - - - {showUsers && ( - - )} - {visibleRoles.length && visibleDBNames.length - ? visibleDBNames.map(name => ( - - )) - : null} - - - - {visibleRoles.length ? ( - visibleRoles.map((role, roleIndex) => ( + {visibleRoles.length ? ( + +
RoleUsers - {name} -
+ + + + {showUsers && ( + + )} + {visibleDBNames.length + ? visibleDBNames.map(name => ( + + )) + : null} + + + + {visibleRoles.map((role, roleIndex) => ( - )) - ) : ( - - )} - -
RoleUsers + {name} +
-
+ ))} + + + + ) : ( + + )}
diff --git a/ui/src/admin/containers/influxdb/UsersPage.tsx b/ui/src/admin/containers/influxdb/UsersPage.tsx index edd4d373a..0c69b4013 100644 --- a/ui/src/admin/containers/influxdb/UsersPage.tsx +++ b/ui/src/admin/containers/influxdb/UsersPage.tsx @@ -18,7 +18,7 @@ import AdminInfluxDBTabbedPage, { isConnectedToLDAP, } from './AdminInfluxDBTabbedPage' import FancyScrollbar from 'src/shared/components/FancyScrollbar' -import EmptyRow from 'src/admin/components/EmptyRow' +import NoEntities from 'src/admin/components/influxdb/NoEntities' import UserRow from 'src/admin/components/UserRow' import useDebounce from 'src/utils/useDebounce' import useChangeEffect from 'src/utils/useChangeEffect' @@ -218,32 +218,32 @@ const UsersPage = ({
- - - - - - {showRoles && ( - - )} - {visibleUsers.length && visibleDBNames.length - ? visibleDBNames.map(name => ( - - )) - : null} - - - - {visibleUsers.length ? ( - visibleUsers.map((user, userIndex) => ( + {visibleUsers.length ? ( + +
User - {isEnterprise ? 'Roles' : 'Admin'} - - {name} -
+ + + + {showRoles && ( + + )} + {visibleDBNames.length + ? visibleDBNames.map(name => ( + + )) + : null} + + + + {visibleUsers.map((user, userIndex) => ( - )) - ) : ( - - )} - -
User + {isEnterprise ? 'Roles' : 'Admin'} + + {name} +
-
+ ))} + + + + ) : ( + + )}
diff --git a/ui/src/style/components/tables.scss b/ui/src/style/components/tables.scss index 7d357196b..e7db2e581 100644 --- a/ui/src/style/components/tables.scss +++ b/ui/src/style/components/tables.scss @@ -113,6 +113,11 @@ tr.table-empty-state, } } } +p.empty { + font-weight: 400; + font-size: 18px; + color: $g9-mountain; +} /* Table Tabs From a5ea4c6ff4e285367ebf5d63626043bdf1197c04 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 16 Jun 2022 20:20:24 +0200 Subject: [PATCH 17/18] fix(ui): style no entities messsage --- ui/src/style/components/tables.scss | 5 ----- ui/src/style/pages/admin.scss | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/src/style/components/tables.scss b/ui/src/style/components/tables.scss index e7db2e581..7d357196b 100644 --- a/ui/src/style/components/tables.scss +++ b/ui/src/style/components/tables.scss @@ -113,11 +113,6 @@ tr.table-empty-state, } } } -p.empty { - font-weight: 400; - font-size: 18px; - color: $g9-mountain; -} /* Table Tabs diff --git a/ui/src/style/pages/admin.scss b/ui/src/style/pages/admin.scss index e201c05d6..ef5b93753 100644 --- a/ui/src/style/pages/admin.scss +++ b/ui/src/style/pages/admin.scss @@ -153,6 +153,12 @@ pre.admin-table--query { padding: 0 30px; min-height: 60px; } + p.empty { + font-weight: 400; + font-size: 18px; + color: $g9-mountain; + } + .influxdb-admin--contents{ height: calc(100%-60px); } From 0c047c4566d4a5c969527370c9821d3f1870b580 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 16 Jun 2022 20:46:16 +0200 Subject: [PATCH 18/18] fix(cypress): adjust tests --- ui/cypress/integration/admin.test.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ui/cypress/integration/admin.test.ts b/ui/cypress/integration/admin.test.ts index 6680507d4..f0dfc2cf5 100644 --- a/ui/cypress/integration/admin.test.ts +++ b/ui/cypress/integration/admin.test.ts @@ -238,15 +238,6 @@ describe('Use Admin tab', () => { }) it('create a role, edit it, assign it to a user, and delete it', () => { - cy.getByTestID('admin-table--head').within(() => { - cy.get('th').contains('Users').should('exist') - }) - - cy.getByTestID('hide-users--toggle').click() - cy.getByTestID('admin-table--head').within(() => { - cy.get('th').contains('Users').should('not.exist') - }) - cy.getByTestID(`role-${influxDB.role.name}--row`).should('not.exist') cy.getByTestID('create-role--button').click({force: true}) cy.getByTestID('dismiss-button').click()