github.com/sequix/cortex@v1.1.6/pkg/ruler/api.go (about) 1 package ruler 2 3 import ( 4 "database/sql" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 9 "github.com/go-kit/kit/log/level" 10 "github.com/gorilla/mux" 11 12 "github.com/sequix/cortex/pkg/configs" 13 "github.com/sequix/cortex/pkg/configs/db" 14 "github.com/sequix/cortex/pkg/util" 15 "github.com/weaveworks/common/user" 16 ) 17 18 // API implements the configs api. 19 type API struct { 20 db db.DB 21 http.Handler 22 } 23 24 // NewAPIFromConfig makes a new API from our database config. 25 func NewAPIFromConfig(cfg db.Config) (*API, error) { 26 db, err := db.New(cfg) 27 if err != nil { 28 return nil, err 29 } 30 return NewAPI(db), nil 31 } 32 33 // NewAPI creates a new API. 34 func NewAPI(db db.DB) *API { 35 a := &API{db: db} 36 r := mux.NewRouter() 37 a.RegisterRoutes(r) 38 a.Handler = r 39 return a 40 } 41 42 // RegisterRoutes registers the configs API HTTP routes with the provided Router. 43 func (a *API) RegisterRoutes(r *mux.Router) { 44 for _, route := range []struct { 45 name, method, path string 46 handler http.HandlerFunc 47 }{ 48 {"get_rules", "GET", "/api/prom/rules", a.getConfig}, 49 {"cas_rules", "POST", "/api/prom/rules", a.casConfig}, 50 } { 51 r.Handle(route.path, route.handler).Methods(route.method).Name(route.name) 52 } 53 } 54 55 // getConfig returns the request configuration. 56 func (a *API) getConfig(w http.ResponseWriter, r *http.Request) { 57 userID, _, err := user.ExtractOrgIDFromHTTPRequest(r) 58 if err != nil { 59 http.Error(w, err.Error(), http.StatusUnauthorized) 60 return 61 } 62 logger := util.WithContext(r.Context(), util.Logger) 63 64 cfg, err := a.db.GetRulesConfig(r.Context(), userID) 65 if err == sql.ErrNoRows { 66 http.Error(w, "No configuration", http.StatusNotFound) 67 return 68 } else if err != nil { 69 level.Error(logger).Log("msg", "error getting config", "err", err) 70 http.Error(w, err.Error(), http.StatusInternalServerError) 71 return 72 } 73 74 w.Header().Set("Content-Type", "application/json") 75 if err := json.NewEncoder(w).Encode(cfg); err != nil { 76 level.Error(logger).Log("msg", "error encoding config", "err", err) 77 http.Error(w, err.Error(), http.StatusInternalServerError) 78 return 79 } 80 } 81 82 type configUpdateRequest struct { 83 OldConfig configs.RulesConfig `json:"old_config"` 84 NewConfig configs.RulesConfig `json:"new_config"` 85 } 86 87 func (a *API) casConfig(w http.ResponseWriter, r *http.Request) { 88 userID, _, err := user.ExtractOrgIDFromHTTPRequest(r) 89 if err != nil { 90 http.Error(w, err.Error(), http.StatusUnauthorized) 91 return 92 } 93 logger := util.WithContext(r.Context(), util.Logger) 94 95 var updateReq configUpdateRequest 96 if err := json.NewDecoder(r.Body).Decode(&updateReq); err != nil { 97 level.Error(logger).Log("msg", "error decoding json body", "err", err) 98 http.Error(w, err.Error(), http.StatusBadRequest) 99 return 100 } 101 102 if _, err = updateReq.NewConfig.Parse(); err != nil { 103 level.Error(logger).Log("msg", "invalid rules", "err", err) 104 http.Error(w, fmt.Sprintf("Invalid rules: %v", err), http.StatusBadRequest) 105 return 106 } 107 108 updated, err := a.db.SetRulesConfig(r.Context(), userID, updateReq.OldConfig, updateReq.NewConfig) 109 if err != nil { 110 level.Error(logger).Log("msg", "error storing config", "err", err) 111 http.Error(w, err.Error(), http.StatusInternalServerError) 112 return 113 } 114 if !updated { 115 http.Error(w, "Supplied configuration doesn't match current configuration", http.StatusConflict) 116 } 117 w.WriteHeader(http.StatusNoContent) 118 }