github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/chunk/purger/request_handler.go (about)

     1  package purger
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"time"
     8  
     9  	"github.com/go-kit/log/level"
    10  
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	"github.com/prometheus/client_golang/prometheus/promauto"
    13  	"github.com/prometheus/common/model"
    14  	"github.com/prometheus/prometheus/promql/parser"
    15  
    16  	"github.com/cortexproject/cortex/pkg/tenant"
    17  	"github.com/cortexproject/cortex/pkg/util"
    18  	util_log "github.com/cortexproject/cortex/pkg/util/log"
    19  )
    20  
    21  type deleteRequestHandlerMetrics struct {
    22  	deleteRequestsReceivedTotal *prometheus.CounterVec
    23  }
    24  
    25  func newDeleteRequestHandlerMetrics(r prometheus.Registerer) *deleteRequestHandlerMetrics {
    26  	m := deleteRequestHandlerMetrics{}
    27  
    28  	m.deleteRequestsReceivedTotal = promauto.With(r).NewCounterVec(prometheus.CounterOpts{
    29  		Namespace: "cortex",
    30  		Name:      "purger_delete_requests_received_total",
    31  		Help:      "Number of delete requests received per user",
    32  	}, []string{"user"})
    33  
    34  	return &m
    35  }
    36  
    37  // DeleteRequestHandler provides handlers for delete requests
    38  type DeleteRequestHandler struct {
    39  	deleteStore               *DeleteStore
    40  	metrics                   *deleteRequestHandlerMetrics
    41  	deleteRequestCancelPeriod time.Duration
    42  }
    43  
    44  // NewDeleteRequestHandler creates a DeleteRequestHandler
    45  func NewDeleteRequestHandler(deleteStore *DeleteStore, deleteRequestCancelPeriod time.Duration, registerer prometheus.Registerer) *DeleteRequestHandler {
    46  	deleteMgr := DeleteRequestHandler{
    47  		deleteStore:               deleteStore,
    48  		deleteRequestCancelPeriod: deleteRequestCancelPeriod,
    49  		metrics:                   newDeleteRequestHandlerMetrics(registerer),
    50  	}
    51  
    52  	return &deleteMgr
    53  }
    54  
    55  // AddDeleteRequestHandler handles addition of new delete request
    56  func (dm *DeleteRequestHandler) AddDeleteRequestHandler(w http.ResponseWriter, r *http.Request) {
    57  	ctx := r.Context()
    58  	userID, err := tenant.TenantID(ctx)
    59  	if err != nil {
    60  		http.Error(w, err.Error(), http.StatusBadRequest)
    61  		return
    62  	}
    63  
    64  	params := r.URL.Query()
    65  	match := params["match[]"]
    66  	if len(match) == 0 {
    67  		http.Error(w, "selectors not set", http.StatusBadRequest)
    68  		return
    69  	}
    70  
    71  	for i := range match {
    72  		_, err := parser.ParseMetricSelector(match[i])
    73  		if err != nil {
    74  			http.Error(w, err.Error(), http.StatusBadRequest)
    75  			return
    76  		}
    77  	}
    78  
    79  	startParam := params.Get("start")
    80  	startTime := int64(0)
    81  	if startParam != "" {
    82  		startTime, err = util.ParseTime(startParam)
    83  		if err != nil {
    84  			http.Error(w, err.Error(), http.StatusBadRequest)
    85  			return
    86  		}
    87  	}
    88  
    89  	endParam := params.Get("end")
    90  	endTime := int64(model.Now())
    91  
    92  	if endParam != "" {
    93  		endTime, err = util.ParseTime(endParam)
    94  		if err != nil {
    95  			http.Error(w, err.Error(), http.StatusBadRequest)
    96  			return
    97  		}
    98  
    99  		if endTime > int64(model.Now()) {
   100  			http.Error(w, "deletes in future not allowed", http.StatusBadRequest)
   101  			return
   102  		}
   103  	}
   104  
   105  	if startTime > endTime {
   106  		http.Error(w, "start time can't be greater than end time", http.StatusBadRequest)
   107  		return
   108  	}
   109  
   110  	if err := dm.deleteStore.AddDeleteRequest(ctx, userID, model.Time(startTime), model.Time(endTime), match); err != nil {
   111  		level.Error(util_log.Logger).Log("msg", "error adding delete request to the store", "err", err)
   112  		http.Error(w, err.Error(), http.StatusInternalServerError)
   113  		return
   114  	}
   115  
   116  	dm.metrics.deleteRequestsReceivedTotal.WithLabelValues(userID).Inc()
   117  	w.WriteHeader(http.StatusNoContent)
   118  }
   119  
   120  // GetAllDeleteRequestsHandler handles get all delete requests
   121  func (dm *DeleteRequestHandler) GetAllDeleteRequestsHandler(w http.ResponseWriter, r *http.Request) {
   122  	ctx := r.Context()
   123  	userID, err := tenant.TenantID(ctx)
   124  	if err != nil {
   125  		http.Error(w, err.Error(), http.StatusBadRequest)
   126  		return
   127  	}
   128  
   129  	deleteRequests, err := dm.deleteStore.GetAllDeleteRequestsForUser(ctx, userID)
   130  	if err != nil {
   131  		level.Error(util_log.Logger).Log("msg", "error getting delete requests from the store", "err", err)
   132  		http.Error(w, err.Error(), http.StatusInternalServerError)
   133  		return
   134  	}
   135  
   136  	if err := json.NewEncoder(w).Encode(deleteRequests); err != nil {
   137  		level.Error(util_log.Logger).Log("msg", "error marshalling response", "err", err)
   138  		http.Error(w, fmt.Sprintf("Error marshalling response: %v", err), http.StatusInternalServerError)
   139  	}
   140  }
   141  
   142  // CancelDeleteRequestHandler handles delete request cancellation
   143  func (dm *DeleteRequestHandler) CancelDeleteRequestHandler(w http.ResponseWriter, r *http.Request) {
   144  	ctx := r.Context()
   145  	userID, err := tenant.TenantID(ctx)
   146  	if err != nil {
   147  		http.Error(w, err.Error(), http.StatusBadRequest)
   148  		return
   149  	}
   150  
   151  	params := r.URL.Query()
   152  	requestID := params.Get("request_id")
   153  
   154  	deleteRequest, err := dm.deleteStore.GetDeleteRequest(ctx, userID, requestID)
   155  	if err != nil {
   156  		level.Error(util_log.Logger).Log("msg", "error getting delete request from the store", "err", err)
   157  		http.Error(w, err.Error(), http.StatusInternalServerError)
   158  		return
   159  	}
   160  
   161  	if deleteRequest == nil {
   162  		http.Error(w, "could not find delete request with given id", http.StatusBadRequest)
   163  		return
   164  	}
   165  
   166  	if deleteRequest.Status != StatusReceived {
   167  		http.Error(w, "deletion of request which is in process or already processed is not allowed", http.StatusBadRequest)
   168  		return
   169  	}
   170  
   171  	if deleteRequest.CreatedAt.Add(dm.deleteRequestCancelPeriod).Before(model.Now()) {
   172  		http.Error(w, fmt.Sprintf("deletion of request past the deadline of %s since its creation is not allowed", dm.deleteRequestCancelPeriod.String()), http.StatusBadRequest)
   173  		return
   174  	}
   175  
   176  	if err := dm.deleteStore.RemoveDeleteRequest(ctx, userID, requestID, deleteRequest.CreatedAt, deleteRequest.StartTime, deleteRequest.EndTime); err != nil {
   177  		level.Error(util_log.Logger).Log("msg", "error cancelling the delete request", "err", err)
   178  		http.Error(w, err.Error(), http.StatusInternalServerError)
   179  		return
   180  	}
   181  
   182  	w.WriteHeader(http.StatusNoContent)
   183  }