package influxql import ( "context" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "github.com/influxdata/flux" "github.com/influxdata/influxdb/kit/tracing" "github.com/influxdata/influxdb/query" ) // Endpoint contains the necessary information to connect to a specific cluster. type Endpoint struct { URL string `json:"url"` Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` } // Service is a client for the influxdb 1.x endpoint that implements the QueryService // for the influxql compiler type. type Service struct { // Endpoints maps a cluster name to the influxdb 1.x endpoint. Endpoints map[string]Endpoint } // Query will execute a query for the influxql.Compiler type against an influxdb 1.x endpoint, // and return results using the default decoder. func (s *Service) Query(ctx context.Context, req *query.Request) (flux.ResultIterator, error) { span, ctx := tracing.StartSpanFromContext(ctx) defer span.Finish() resp, err := s.query(ctx, req) if err != nil { return nil, tracing.LogError(span, err) } // Decode the response into the JSON structure. var results Response if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { return nil, tracing.LogError(span, err) } // Return a result iterator using the response. return NewResponseIterator(&results), nil } // QueryRawJSON will execute a query for the influxql.Compiler type against an influxdb 1.x endpoint, // and return the body of the response as a byte array. func (s *Service) QueryRawJSON(ctx context.Context, req *query.Request) ([]byte, error) { span, ctx := tracing.StartSpanFromContext(ctx) defer span.Finish() resp, err := s.query(ctx, req) if err != nil { return nil, tracing.LogError(span, err) } b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, tracing.LogError(span, err) } return b, nil } func (s *Service) query(ctx context.Context, req *query.Request) (*http.Response, error) { span, ctx := tracing.StartSpanFromContext(ctx) defer span.Finish() // Verify that this is an influxql query in the compiler. compiler, ok := req.Compiler.(*Compiler) if !ok { err := fmt.Errorf("influxql query service does not support the '%s' compiler type", req.Compiler.CompilerType()) return nil, tracing.LogError(span, err) } // Lookup the endpoint information for the cluster. endpoint, ok := s.Endpoints[compiler.Cluster] if !ok { err := fmt.Errorf("no endpoint found for cluster %s", compiler.Cluster) return nil, tracing.LogError(span, err) } // Prepare the HTTP request. u, err := url.Parse(endpoint.URL) if err != nil { return nil, tracing.LogError(span, err) } u.Path += "/query" params := url.Values{} params.Set("q", compiler.Query) if compiler.DB != "" { params.Set("db", compiler.DB) } if compiler.RP != "" { params.Set("rp", compiler.RP) } u.RawQuery = params.Encode() hreq, err := http.NewRequest("POST", u.String(), nil) if err != nil { return nil, tracing.LogError(span, err) } hreq = hreq.WithContext(ctx) hreq.SetBasicAuth(endpoint.Username, endpoint.Password) tracing.InjectToHTTPRequest(span, hreq) // Perform the request and look at the status code. resp, err := http.DefaultClient.Do(hreq) if err != nil { return nil, tracing.LogError(span, err) } else if resp.StatusCode/100 != 2 { err = fmt.Errorf("unexpected http status: %s", resp.Status) return nil, tracing.LogError(span, err) } return resp, nil }