github.com/kubewharf/katalyst-core@v0.5.3/pkg/controller/resource-recommend/datasource/prometheus/prometheus_provider.go (about)

     1  /*
     2  Copyright 2022 The Katalyst Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package prometheus
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	promapiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
    25  	datamodel "github.com/prometheus/common/model"
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/klog/v2"
    28  
    29  	"github.com/kubewharf/katalyst-core/pkg/controller/resource-recommend/datasource"
    30  	"github.com/kubewharf/katalyst-core/pkg/util/general"
    31  	datasourcetypes "github.com/kubewharf/katalyst-core/pkg/util/resource-recommend/types/datasource"
    32  )
    33  
    34  type prometheus struct {
    35  	promAPIClient promapiv1.API
    36  	config        *PromConfig
    37  }
    38  
    39  type PromDatasource interface {
    40  	datasource.Datasource
    41  	GetPromClient() promapiv1.API
    42  }
    43  
    44  // NewPrometheus return a prometheus data source
    45  func NewPrometheus(config *PromConfig) (PromDatasource, error) {
    46  	client, err := NewPrometheusClient(config)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	promAPIClient := promapiv1.NewAPI(client)
    51  
    52  	return &prometheus{promAPIClient: promAPIClient, config: config}, nil
    53  }
    54  
    55  func (p *prometheus) GetPromClient() promapiv1.API {
    56  	return p.promAPIClient
    57  }
    58  
    59  func (p *prometheus) ConvertMetricToQuery(metric datasourcetypes.Metric) (*datasourcetypes.Query, error) {
    60  	extraFilters := GetExtraFilters(metric.Selectors, p.config.BaseFilter)
    61  	var queryExpr string
    62  	switch metric.Resource {
    63  	case v1.ResourceCPU:
    64  		queryExpr = GetContainerCpuUsageQueryExp(metric.Namespace, metric.WorkloadName, metric.Kind, metric.ContainerName, extraFilters)
    65  	case v1.ResourceMemory:
    66  		queryExpr = GetContainerMemUsageQueryExp(metric.Namespace, metric.WorkloadName, metric.Kind, metric.ContainerName, extraFilters)
    67  	default:
    68  		return nil, fmt.Errorf("query for resource type %v is not supported", metric.Resource)
    69  	}
    70  	convertedQuery := datasourcetypes.PrometheusQuery{
    71  		Query: queryExpr,
    72  	}
    73  	return &datasourcetypes.Query{
    74  		Prometheus: &convertedQuery,
    75  	}, nil
    76  }
    77  
    78  func (p *prometheus) QueryTimeSeries(query *datasourcetypes.Query, start time.Time, end time.Time, step time.Duration) (*datasourcetypes.TimeSeries, error) {
    79  	klog.InfoS("QueryTimeSeries", "query", general.StructToString(query), "start", start, "end", end)
    80  	timeoutCtx, cancelFunc := context.WithTimeout(context.Background(), p.config.Timeout)
    81  	defer cancelFunc()
    82  	timeSeries, err := p.queryRangeSync(timeoutCtx, query.Prometheus.Query, start, end, step)
    83  	if err != nil {
    84  		klog.ErrorS(err, "query", query, "start", start, "end", end)
    85  		return nil, err
    86  	}
    87  	return timeSeries, nil
    88  }
    89  
    90  // queryRangeSync range query prometheus in sync way
    91  func (p *prometheus) queryRangeSync(ctx context.Context, query string, start, end time.Time, step time.Duration) (*datasourcetypes.TimeSeries, error) {
    92  	r := promapiv1.Range{
    93  		Start: start,
    94  		End:   end,
    95  		Step:  step,
    96  	}
    97  	klog.InfoS("Prom query", "query", query)
    98  	var ts *datasourcetypes.TimeSeries
    99  	results, warnings, err := p.promAPIClient.QueryRange(ctx, query, r)
   100  	if len(warnings) != 0 {
   101  		klog.InfoS("Prom query range warnings", "warnings", warnings)
   102  	}
   103  	if err != nil {
   104  		return ts, err
   105  	}
   106  	klog.V(5).InfoS("Prom query range result", "query", query, "result", results.String(), "resultsType", results.Type())
   107  
   108  	return p.convertPromResultsToTimeSeries(results)
   109  }
   110  
   111  func (p *prometheus) convertPromResultsToTimeSeries(value datamodel.Value) (*datasourcetypes.TimeSeries, error) {
   112  	results := datasourcetypes.NewTimeSeries()
   113  	typeValue := value.Type()
   114  	switch typeValue {
   115  	case datamodel.ValMatrix:
   116  		if matrix, ok := value.(datamodel.Matrix); ok {
   117  			for _, sampleStream := range matrix {
   118  				if sampleStream == nil {
   119  					continue
   120  				}
   121  				for key, val := range sampleStream.Metric {
   122  					results.AppendLabel(string(key), string(val))
   123  				}
   124  				for _, pair := range sampleStream.Values {
   125  					results.AppendSample(int64(pair.Timestamp/1000), float64(pair.Value))
   126  				}
   127  			}
   128  			return results, nil
   129  		} else {
   130  			return results, fmt.Errorf("prometheus value type is %v, but assert failed", typeValue)
   131  		}
   132  
   133  	case datamodel.ValVector:
   134  		if vector, ok := value.(datamodel.Vector); ok {
   135  			for _, sample := range vector {
   136  				if sample == nil {
   137  					continue
   138  				}
   139  				for key, val := range sample.Metric {
   140  					results.AppendLabel(string(key), string(val))
   141  				}
   142  				results.AppendSample(int64(sample.Timestamp/1000), float64(sample.Value))
   143  			}
   144  			return results, nil
   145  		} else {
   146  			return results, fmt.Errorf("prometheus value type is %v, but assert failed", typeValue)
   147  		}
   148  	}
   149  	return results, fmt.Errorf("prometheus return unsupported model value type %v", typeValue)
   150  }