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 }