github.com/kubewharf/katalyst-core@v0.5.3/pkg/custom-metric/store/local/server.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 local
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"net/http"
    24  	"strconv"
    25  	"strings"
    26  	"time"
    27  
    28  	"k8s.io/apimachinery/pkg/labels"
    29  	"k8s.io/apimachinery/pkg/runtime/schema"
    30  	"k8s.io/klog/v2"
    31  
    32  	"github.com/kubewharf/katalyst-core/pkg/custom-metric/store/data"
    33  	"github.com/kubewharf/katalyst-core/pkg/custom-metric/store/data/types"
    34  )
    35  
    36  const (
    37  	ServingListPath = "/store/list"
    38  	ServingGetPath  = "/store/get"
    39  	ServingSetPath  = "/store/set"
    40  )
    41  
    42  const (
    43  	StoreListParamObjected = "objected"
    44  )
    45  
    46  const (
    47  	StoreGETParamNamespace       = "namespace"
    48  	StoreGETParamMetricName      = "metricName"
    49  	StoreGETParamMetricSelector  = "metricSelector"
    50  	StoreGETParamObjectGR        = "objGR"
    51  	StoreGETParamObjectName      = "objName"
    52  	StoreGETParamMObjectSelector = "objSelector"
    53  	StoreGETParamLatest          = "latest"
    54  )
    55  
    56  type MemoryStoreData struct{}
    57  
    58  // Serve todo: support to use gzip to reduce the transported data through http
    59  func (l *LocalMemoryMetricStore) Serve(mux *http.ServeMux) {
    60  	klog.Infof("local store add serve handler")
    61  
    62  	mux.HandleFunc(ServingListPath, l.handleMetricList)
    63  	mux.HandleFunc(ServingGetPath, l.handleMetricGet)
    64  	mux.HandleFunc(ServingSetPath, l.handleMetricSet)
    65  }
    66  
    67  func (l *LocalMemoryMetricStore) handleMetricList(w http.ResponseWriter, r *http.Request) {
    68  	if !l.syncSuccess {
    69  		w.WriteHeader(http.StatusNotAcceptable)
    70  		_, _ = fmt.Fprintf(w, "store is in initializing status")
    71  		return
    72  	}
    73  
    74  	klog.V(6).Infof("receive list requests")
    75  
    76  	if r == nil || r.Method != "GET" || r.URL == nil {
    77  		klog.Errorf("Request must be GET with Query URL")
    78  		w.WriteHeader(http.StatusBadRequest)
    79  		_, _ = fmt.Fprintf(w, "Request must be GET with Query URL")
    80  		return
    81  	}
    82  
    83  	start := time.Now()
    84  
    85  	var (
    86  		err             error
    87  		metricMetalList []types.MetricMeta
    88  	)
    89  	if r.URL.Query() == nil || len(getQueryParam(r, StoreListParamObjected)) == 0 {
    90  		metricMetalList, err = l.ListMetricMeta(context.Background(), false)
    91  	} else {
    92  		metricMetalList, err = l.ListMetricMeta(context.Background(), true)
    93  	}
    94  
    95  	if err != nil {
    96  		klog.Errorf("get internal list err: %v", err)
    97  		w.WriteHeader(http.StatusNotAcceptable)
    98  		_, _ = fmt.Fprintf(w, "Marshal internal err: %v", err)
    99  		return
   100  	}
   101  
   102  	readFinished := time.Now()
   103  
   104  	bytes, err := json.Marshal(metricMetalList)
   105  	if err != nil {
   106  		klog.Errorf("marshal internal list err: %v", err)
   107  		w.WriteHeader(http.StatusNotAcceptable)
   108  		_, _ = fmt.Fprintf(w, "Marshal internal error: %v", err)
   109  		return
   110  	}
   111  
   112  	jsonMarshalFinished := time.Now()
   113  
   114  	w.WriteHeader(http.StatusOK)
   115  	_, _ = w.Write(bytes)
   116  
   117  	writeRespFinished := time.Now()
   118  
   119  	klog.V(6).Infof("list cost read: %v, json: %v, resp: %v, total %v; len %v",
   120  		readFinished.Sub(start),
   121  		jsonMarshalFinished.Sub(readFinished),
   122  		writeRespFinished.Sub(jsonMarshalFinished),
   123  		writeRespFinished.Sub(start),
   124  		len(metricMetalList))
   125  }
   126  
   127  func (l *LocalMemoryMetricStore) handleMetricGet(w http.ResponseWriter, r *http.Request) {
   128  	if !l.syncSuccess {
   129  		w.WriteHeader(http.StatusNotAcceptable)
   130  		_, _ = fmt.Fprintf(w, "store is in initializing status")
   131  		return
   132  	}
   133  
   134  	klog.V(6).Infof("receive get requests")
   135  
   136  	if r == nil || r.Method != "GET" || r.URL == nil || r.URL.Query() == nil {
   137  		klog.Errorf("Request must be GET with Query URL")
   138  		w.WriteHeader(http.StatusBadRequest)
   139  		_, _ = fmt.Fprintf(w, "Request must be GET with Query URL")
   140  		return
   141  	}
   142  
   143  	start := time.Now()
   144  
   145  	// 1. parse parameters from URL Query
   146  	var (
   147  		objGR          *schema.GroupResource
   148  		objSelector    labels.Selector = nil
   149  		metricSelector labels.Selector = nil
   150  		latest         bool
   151  	)
   152  
   153  	namespace := getQueryParam(r, StoreGETParamNamespace)
   154  
   155  	metricName := getQueryParam(r, StoreGETParamMetricName)
   156  	metricSelectorStr := getQueryParam(r, StoreGETParamMetricSelector)
   157  	if len(metricSelectorStr) > 0 {
   158  		selector, err := labels.Parse(metricSelectorStr)
   159  		if err != nil {
   160  			klog.Errorf("metric selector parsing err: %v", err)
   161  			w.WriteHeader(http.StatusBadRequest)
   162  			_, _ = fmt.Fprintf(w, "Item selector parsing %v err: %v", metricSelectorStr, err)
   163  			return
   164  		}
   165  		metricSelector = selector
   166  	}
   167  
   168  	objName := getQueryParam(r, StoreGETParamObjectName)
   169  	objGRStr := getQueryParam(r, StoreGETParamObjectGR)
   170  	if len(objGRStr) > 0 {
   171  		_, gr := schema.ParseResourceArg(objGRStr)
   172  		objGR = &gr
   173  	}
   174  	objSelectorStr := getQueryParam(r, StoreGETParamMObjectSelector)
   175  	if len(objSelectorStr) > 0 {
   176  		selector, err := labels.Parse(objSelectorStr)
   177  		if err != nil {
   178  			klog.Errorf("object selector parsing err: %v", err)
   179  			w.WriteHeader(http.StatusBadRequest)
   180  			_, _ = fmt.Fprintf(w, "Object selector parsing %v err: %v", metricSelectorStr, err)
   181  			return
   182  		}
   183  		objSelector = selector
   184  	}
   185  
   186  	latestStr := getQueryParam(r, StoreGETParamLatest)
   187  	if len(latestStr) > 0 {
   188  		var err error
   189  		latest, err = strconv.ParseBool(latestStr)
   190  		if err != nil {
   191  			klog.Errorf("limited parsing %v err: %v", latestStr, err)
   192  			w.WriteHeader(http.StatusBadRequest)
   193  			_, _ = fmt.Fprintf(w, "Limited parsing %v err %v", latestStr, err)
   194  			return
   195  		}
   196  	}
   197  
   198  	readFinished := time.Now()
   199  
   200  	// 2. get from local cache and unmarshal into writer
   201  	internalList, err := l.GetMetric(context.Background(), namespace, metricName, objName, objGR, objSelector, metricSelector, latest)
   202  	if err != nil {
   203  		klog.Errorf("get internal list err: %v", err)
   204  		w.WriteHeader(http.StatusNotAcceptable)
   205  		_, _ = fmt.Fprintf(w, "Marshal internal err: %v", err)
   206  		return
   207  	}
   208  
   209  	bytes, err := json.Marshal(internalList)
   210  	if err != nil {
   211  		klog.Errorf("marshal internal list err: %v", err)
   212  		w.WriteHeader(http.StatusNotAcceptable)
   213  		_, _ = fmt.Fprintf(w, "Marshal internal err: %v", err)
   214  		return
   215  	}
   216  
   217  	jsonMarshalFinished := time.Now()
   218  
   219  	w.WriteHeader(http.StatusOK)
   220  	_, _ = w.Write(bytes)
   221  
   222  	writeRespFinished := time.Now()
   223  
   224  	klog.Infof("get metric %v, obj %v, cost read: %v, json: %v, resp: %v, total %v; len %v",
   225  		metricName, objName,
   226  		readFinished.Sub(start),
   227  		jsonMarshalFinished.Sub(readFinished),
   228  		writeRespFinished.Sub(jsonMarshalFinished),
   229  		writeRespFinished.Sub(start),
   230  		len(internalList))
   231  }
   232  
   233  func (l *LocalMemoryMetricStore) handleMetricSet(w http.ResponseWriter, r *http.Request) {
   234  	if !l.syncSuccess {
   235  		w.WriteHeader(http.StatusNotAcceptable)
   236  		_, _ = fmt.Fprintf(w, "store is in initializing status")
   237  		return
   238  	}
   239  
   240  	klog.V(6).Infof("receive set requests")
   241  
   242  	if r == nil || r.Method != "POST" || r.Body == nil {
   243  		klog.Errorf("Request must be POST with Body")
   244  		w.WriteHeader(http.StatusBadRequest)
   245  		_, _ = fmt.Fprintf(w, "Request must be POST with Body")
   246  		return
   247  	}
   248  
   249  	start := time.Now()
   250  
   251  	defer func() {
   252  		_ = r.Body.Close()
   253  	}()
   254  	var seriesList []*data.MetricSeries
   255  	if err := json.NewDecoder(r.Body).Decode(&seriesList); err != nil {
   256  		klog.Errorf("read body err: %v", err)
   257  		w.WriteHeader(http.StatusBadRequest)
   258  		_, _ = fmt.Fprintf(w, "Read body err: %v", err)
   259  		return
   260  	}
   261  
   262  	jsonMarshalFinished := time.Now()
   263  
   264  	if err := l.InsertMetric(seriesList); err != nil {
   265  		klog.Errorf("insert seriesList err: %v", err)
   266  		w.WriteHeader(http.StatusNotAcceptable)
   267  		_, _ = fmt.Fprintf(w, "Insert seriesList err: %v", err)
   268  		return
   269  	}
   270  
   271  	writeRespFinished := time.Now()
   272  
   273  	w.WriteHeader(http.StatusOK)
   274  	_, _ = w.Write([]byte("success"))
   275  	klog.V(6).Infof("set cost read&json: %v, resp: %v, total %v",
   276  		jsonMarshalFinished.Sub(start),
   277  		writeRespFinished.Sub(jsonMarshalFinished),
   278  		writeRespFinished.Sub(start))
   279  }
   280  
   281  // getQueryParam is a common util function to trim parameters from http query;
   282  // if we need to perform string trim or anything like that, do it here
   283  func getQueryParam(r *http.Request, key string) string {
   284  	return strings.TrimSpace(r.URL.Query().Get(key))
   285  }