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  }