mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-11-01 00:15:05 +01:00 
			
		
		
		
	Refactor
Port logging to cclog, use loglevels Separate REST API from pkg API
This commit is contained in:
		
							
								
								
									
										177
									
								
								internal/api/memorystore.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								internal/api/memorystore.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | ||||
| // Copyright (C) NHR@FAU, University Erlangen-Nuremberg. | ||||
| // All rights reserved. | ||||
| // Use of this source code is governed by a MIT-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package api | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/ClusterCockpit/cc-backend/internal/memorystore" | ||||
| 	cclog "github.com/ClusterCockpit/cc-lib/ccLogger" | ||||
|  | ||||
| 	"github.com/influxdata/line-protocol/v2/lineprotocol" | ||||
| ) | ||||
|  | ||||
| // handleFree godoc | ||||
| // @summary | ||||
| // @tags free | ||||
| // @description This endpoint allows the users to free the Buffers from the | ||||
| // metric store. This endpoint offers the users to remove then systematically | ||||
| // and also allows then to prune the data under node, if they do not want to | ||||
| // remove the whole node. | ||||
| // @produce     json | ||||
| // @param       to        query    string        false  "up to timestamp" | ||||
| // @success     200            {string} string  "ok" | ||||
| // @failure     400            {object} api.ErrorResponse       "Bad Request" | ||||
| // @failure     401            {object} api.ErrorResponse       "Unauthorized" | ||||
| // @failure     403            {object} api.ErrorResponse       "Forbidden" | ||||
| // @failure     500            {object} api.ErrorResponse       "Internal Server Error" | ||||
| // @security    ApiKeyAuth | ||||
| // @router      /free/ [post] | ||||
| func freeMetrics(rw http.ResponseWriter, r *http.Request) { | ||||
| 	rawTo := r.URL.Query().Get("to") | ||||
| 	if rawTo == "" { | ||||
| 		handleError(errors.New("'to' is a required query parameter"), http.StatusBadRequest, rw) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	to, err := strconv.ParseInt(rawTo, 10, 64) | ||||
| 	if err != nil { | ||||
| 		handleError(err, http.StatusInternalServerError, rw) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// // TODO: lastCheckpoint might be modified by different go-routines. | ||||
| 	// // Load it using the sync/atomic package? | ||||
| 	// freeUpTo := lastCheckpoint.Unix() | ||||
| 	// if to < freeUpTo { | ||||
| 	// 	freeUpTo = to | ||||
| 	// } | ||||
|  | ||||
| 	bodyDec := json.NewDecoder(r.Body) | ||||
| 	var selectors [][]string | ||||
| 	err = bodyDec.Decode(&selectors) | ||||
| 	if err != nil { | ||||
| 		http.Error(rw, err.Error(), http.StatusBadRequest) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	ms := memorystore.GetMemoryStore() | ||||
| 	n := 0 | ||||
| 	for _, sel := range selectors { | ||||
| 		bn, err := ms.Free(sel, to) | ||||
| 		if err != nil { | ||||
| 			handleError(err, http.StatusInternalServerError, rw) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		n += bn | ||||
| 	} | ||||
|  | ||||
| 	rw.WriteHeader(http.StatusOK) | ||||
| 	fmt.Fprintf(rw, "buffers freed: %d\n", n) | ||||
| } | ||||
|  | ||||
| // handleWrite godoc | ||||
| // @summary Receive metrics in InfluxDB line-protocol | ||||
| // @tags write | ||||
| // @description Write data to the in-memory store in the InfluxDB line-protocol using [this format](https://github.com/ClusterCockpit/cc-specifications/blob/master/metrics/lineprotocol_alternative.md) | ||||
|  | ||||
| // @accept      plain | ||||
| // @produce     json | ||||
| // @param       cluster        query string false "If the lines in the body do not have a cluster tag, use this value instead." | ||||
| // @success     200            {string} string  "ok" | ||||
| // @failure     400            {object} api.ErrorResponse       "Bad Request" | ||||
| // @failure     401            {object} api.ErrorResponse       "Unauthorized" | ||||
| // @failure     403            {object} api.ErrorResponse       "Forbidden" | ||||
| // @failure     500            {object} api.ErrorResponse       "Internal Server Error" | ||||
| // @security    ApiKeyAuth | ||||
| // @router      /write/ [post] | ||||
| func writeMetrics(rw http.ResponseWriter, r *http.Request) { | ||||
| 	bytes, err := io.ReadAll(r.Body) | ||||
| 	rw.Header().Add("Content-Type", "application/json") | ||||
| 	if err != nil { | ||||
| 		handleError(err, http.StatusInternalServerError, rw) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	ms := memorystore.GetMemoryStore() | ||||
| 	dec := lineprotocol.NewDecoderWithBytes(bytes) | ||||
| 	if err := memorystore.DecodeLine(dec, ms, r.URL.Query().Get("cluster")); err != nil { | ||||
| 		cclog.Errorf("/api/write error: %s", err.Error()) | ||||
| 		handleError(err, http.StatusBadRequest, rw) | ||||
| 		return | ||||
| 	} | ||||
| 	rw.WriteHeader(http.StatusOK) | ||||
| } | ||||
|  | ||||
| // handleDebug godoc | ||||
| // @summary Debug endpoint | ||||
| // @tags debug | ||||
| // @description This endpoint allows the users to print the content of | ||||
| // nodes/clusters/metrics to review the state of the data. | ||||
| // @produce     json | ||||
| // @param       selector        query    string            false "Selector" | ||||
| // @success     200            {string} string  "Debug dump" | ||||
| // @failure     400            {object} api.ErrorResponse       "Bad Request" | ||||
| // @failure     401            {object} api.ErrorResponse       "Unauthorized" | ||||
| // @failure     403            {object} api.ErrorResponse       "Forbidden" | ||||
| // @failure     500            {object} api.ErrorResponse       "Internal Server Error" | ||||
| // @security    ApiKeyAuth | ||||
| // @router      /debug/ [post] | ||||
| func debugMetrics(rw http.ResponseWriter, r *http.Request) { | ||||
| 	raw := r.URL.Query().Get("selector") | ||||
| 	rw.Header().Add("Content-Type", "application/json") | ||||
| 	selector := []string{} | ||||
| 	if len(raw) != 0 { | ||||
| 		selector = strings.Split(raw, ":") | ||||
| 	} | ||||
|  | ||||
| 	ms := memorystore.GetMemoryStore() | ||||
| 	if err := ms.DebugDump(bufio.NewWriter(rw), selector); err != nil { | ||||
| 		handleError(err, http.StatusBadRequest, rw) | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // handleHealthCheck godoc | ||||
| // @summary HealthCheck endpoint | ||||
| // @tags healthcheck | ||||
| // @description This endpoint allows the users to check if a node is healthy | ||||
| // @produce     json | ||||
| // @param       selector        query    string            false "Selector" | ||||
| // @success     200            {string} string  "Debug dump" | ||||
| // @failure     400            {object} api.ErrorResponse       "Bad Request" | ||||
| // @failure     401            {object} api.ErrorResponse       "Unauthorized" | ||||
| // @failure     403            {object} api.ErrorResponse       "Forbidden" | ||||
| // @failure     500            {object} api.ErrorResponse       "Internal Server Error" | ||||
| // @security    ApiKeyAuth | ||||
| // @router      /healthcheck/ [get] | ||||
| func metricsHealth(rw http.ResponseWriter, r *http.Request) { | ||||
| 	rawCluster := r.URL.Query().Get("cluster") | ||||
| 	rawNode := r.URL.Query().Get("node") | ||||
|  | ||||
| 	if rawCluster == "" || rawNode == "" { | ||||
| 		handleError(errors.New("'cluster' and 'node' are required query parameter"), http.StatusBadRequest, rw) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	rw.Header().Add("Content-Type", "application/json") | ||||
|  | ||||
| 	selector := []string{rawCluster, rawNode} | ||||
|  | ||||
| 	ms := memorystore.GetMemoryStore() | ||||
| 	if err := ms.HealthCheck(bufio.NewWriter(rw), selector); err != nil { | ||||
| 		handleError(err, http.StatusBadRequest, rw) | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
| @@ -15,7 +15,6 @@ import ( | ||||
|  | ||||
| 	"github.com/ClusterCockpit/cc-backend/internal/auth" | ||||
| 	"github.com/ClusterCockpit/cc-backend/internal/config" | ||||
| 	"github.com/ClusterCockpit/cc-backend/internal/memorystore" | ||||
| 	"github.com/ClusterCockpit/cc-backend/internal/repository" | ||||
| 	cclog "github.com/ClusterCockpit/cc-lib/ccLogger" | ||||
| 	"github.com/ClusterCockpit/cc-lib/schema" | ||||
| @@ -98,15 +97,11 @@ func (api *RestApi) MountUserApiRoutes(r *mux.Router) { | ||||
|  | ||||
| func (api *RestApi) MountMetricStoreApiRoutes(r *mux.Router) { | ||||
| 	// REST API Uses TokenAuth | ||||
| 	r.HandleFunc("/api/free", memorystore.HandleFree).Methods(http.MethodPost) | ||||
| 	r.HandleFunc("/api/write", memorystore.HandleWrite).Methods(http.MethodPost) | ||||
| 	r.HandleFunc("/api/debug", memorystore.HandleDebug).Methods(http.MethodGet) | ||||
| 	r.HandleFunc("/api/healthcheck", memorystore.HandleHealthCheck).Methods(http.MethodGet) | ||||
| 	// Refactor | ||||
| 	r.HandleFunc("/api/free/", memorystore.HandleFree).Methods(http.MethodPost) | ||||
| 	r.HandleFunc("/api/write/", memorystore.HandleWrite).Methods(http.MethodPost) | ||||
| 	r.HandleFunc("/api/debug/", memorystore.HandleDebug).Methods(http.MethodGet) | ||||
| 	r.HandleFunc("/api/healthcheck/", memorystore.HandleHealthCheck).Methods(http.MethodGet) | ||||
| 	// Refactor ?? | ||||
| 	r.HandleFunc("/api/free", freeMetrics).Methods(http.MethodPost) | ||||
| 	r.HandleFunc("/api/write", writeMetrics).Methods(http.MethodPost) | ||||
| 	r.HandleFunc("/api/debug", debugMetrics).Methods(http.MethodGet) | ||||
| 	r.HandleFunc("/api/healthcheck", metricsHealth).Methods(http.MethodGet) | ||||
| } | ||||
|  | ||||
| func (api *RestApi) MountConfigApiRoutes(r *mux.Router) { | ||||
| @@ -269,7 +264,7 @@ func (api *RestApi) putMachineState(rw http.ResponseWriter, r *http.Request) { | ||||
| 	cluster := vars["cluster"] | ||||
| 	host := vars["host"] | ||||
| 	dir := filepath.Join(api.MachineStateDir, cluster) | ||||
| 	if err := os.MkdirAll(dir, 0755); err != nil { | ||||
| 	if err := os.MkdirAll(dir, 0o755); err != nil { | ||||
| 		http.Error(rw, err.Error(), http.StatusInternalServerError) | ||||
| 		return | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user