github.com/grafana/pyroscope@v1.18.0/pkg/frontend/readpath/queryfrontend/compat.go (about) 1 package queryfrontend 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "connectrpc.com/connect" 9 "github.com/prometheus/prometheus/model/labels" 10 "github.com/prometheus/prometheus/promql/parser" 11 12 queryv1 "github.com/grafana/pyroscope/api/gen/proto/go/query/v1" 13 phlaremodel "github.com/grafana/pyroscope/pkg/model" 14 "github.com/grafana/pyroscope/pkg/querybackend" 15 "github.com/grafana/pyroscope/pkg/util/connectgrpc" 16 "github.com/grafana/pyroscope/pkg/util/http" 17 ) 18 19 // querySingle is a helper method that expects a single report 20 // of the appropriate type in the response; this method in an 21 // adapter to the old query API. 22 func (q *QueryFrontend) querySingle( 23 ctx context.Context, 24 req *queryv1.QueryRequest, 25 ) (*queryv1.Report, error) { 26 if len(req.Query) != 1 { 27 // Nil report is a valid response. 28 return nil, nil 29 } 30 t := querybackend.QueryReportType(req.Query[0].QueryType) 31 resp, err := q.Query(ctx, req) 32 if err != nil { 33 code, sanitized := http.ClientHTTPStatusAndError(err) 34 return nil, connect.NewError(connectgrpc.HTTPToCode(int32(code)), sanitized) 35 } 36 var r *queryv1.Report 37 for _, x := range resp.Reports { 38 if x.ReportType == t { 39 r = x 40 break 41 } 42 } 43 return r, nil 44 } 45 46 func buildLabelSelectorFromMatchers(matchers []string) (string, error) { 47 parsed, err := parseMatchers(matchers) 48 if err != nil { 49 return "", fmt.Errorf("parsing label selector: %w", err) 50 } 51 return matchersToLabelSelector(parsed), nil 52 } 53 54 func buildLabelSelectorWithProfileType(labelSelector, profileTypeID string) (string, error) { 55 matchers, err := parser.ParseMetricSelector(labelSelector) 56 if err != nil { 57 return "", fmt.Errorf("parsing label selector %q: %w", labelSelector, err) 58 } 59 profileType, err := phlaremodel.ParseProfileTypeSelector(profileTypeID) 60 if err != nil { 61 return "", fmt.Errorf("parsing profile type ID %q: %w", profileTypeID, err) 62 } 63 matchers = append(matchers, phlaremodel.SelectorFromProfileType(profileType)) 64 return matchersToLabelSelector(matchers), nil 65 } 66 67 func parseMatchers(matchers []string) ([]*labels.Matcher, error) { 68 parsed := make([]*labels.Matcher, 0, len(matchers)) 69 for _, m := range matchers { 70 s, err := parser.ParseMetricSelector(m) 71 if err != nil { 72 return nil, fmt.Errorf("failed to parse label selector %q: %w", s, err) 73 } 74 parsed = append(parsed, s...) 75 } 76 return parsed, nil 77 } 78 79 func matchersToLabelSelector(matchers []*labels.Matcher) string { 80 var q strings.Builder 81 q.WriteByte('{') 82 for i, m := range matchers { 83 if i > 0 { 84 q.WriteByte(',') 85 } 86 q.WriteString(m.String()) 87 } 88 q.WriteByte('}') 89 return q.String() 90 }