github.com/influxdata/influxdb/v2@v2.7.6/replications/transport/http.go (about) 1 package transport 2 3 import ( 4 "context" 5 "net/http" 6 7 "github.com/go-chi/chi" 8 "github.com/go-chi/chi/middleware" 9 "github.com/influxdata/influxdb/v2" 10 "github.com/influxdata/influxdb/v2/kit/platform" 11 "github.com/influxdata/influxdb/v2/kit/platform/errors" 12 kithttp "github.com/influxdata/influxdb/v2/kit/transport/http" 13 "github.com/influxdata/influxdb/v2/kv" 14 "github.com/prometheus/client_golang/prometheus" 15 "go.uber.org/zap" 16 ) 17 18 const ( 19 prefixReplications = "/api/v2/replications" 20 ) 21 22 var ( 23 errBadOrg = &errors.Error{ 24 Code: errors.EInvalid, 25 Msg: "invalid or missing org ID", 26 } 27 28 errBadRemoteID = &errors.Error{ 29 Code: errors.EInvalid, 30 Msg: "invalid remote ID", 31 } 32 33 errBadLocalBucketID = &errors.Error{ 34 Code: errors.EInvalid, 35 Msg: "invalid local bucket ID", 36 } 37 38 errBadId = &errors.Error{ 39 Code: errors.EInvalid, 40 Msg: "replication ID is invalid", 41 } 42 ) 43 44 type ReplicationService interface { 45 // ListReplications returns all info about registered replications matching a filter. 46 ListReplications(context.Context, influxdb.ReplicationListFilter) (*influxdb.Replications, error) 47 48 // CreateReplication registers a new replication stream. 49 CreateReplication(context.Context, influxdb.CreateReplicationRequest) (*influxdb.Replication, error) 50 51 // ValidateNewReplication validates that the given settings for a replication are usable, 52 // without persisting the configuration. 53 ValidateNewReplication(context.Context, influxdb.CreateReplicationRequest) error 54 55 // GetReplication returns metadata about the replication with the given ID. 56 GetReplication(context.Context, platform.ID) (*influxdb.Replication, error) 57 58 // UpdateReplication updates the settings for the replication with the given ID. 59 UpdateReplication(context.Context, platform.ID, influxdb.UpdateReplicationRequest) (*influxdb.Replication, error) 60 61 // ValidateUpdatedReplication valdiates that a replication is still usable after applying the 62 // given update, without persisting the new configuration. 63 ValidateUpdatedReplication(context.Context, platform.ID, influxdb.UpdateReplicationRequest) error 64 65 // DeleteReplication deletes all info for the replication with the given ID. 66 DeleteReplication(context.Context, platform.ID) error 67 68 // ValidateReplication checks that the replication with the given ID is still usable with its 69 // persisted settings. 70 ValidateReplication(context.Context, platform.ID) error 71 } 72 73 type ReplicationHandler struct { 74 chi.Router 75 76 log *zap.Logger 77 api *kithttp.API 78 79 replicationsService ReplicationService 80 } 81 82 func NewInstrumentedReplicationHandler(log *zap.Logger, reg prometheus.Registerer, kv kv.Store, svc ReplicationService) *ReplicationHandler { 83 // Collect telemetry 84 svc = newTelemetryCollectingService(kv, svc) 85 // Collect metrics. 86 svc = newMetricCollectingService(reg, svc) 87 // Wrap logging. 88 svc = newLoggingService(log, svc) 89 // Wrap authz. 90 svc = newAuthCheckingService(svc) 91 92 return newReplicationHandler(log, svc) 93 } 94 95 func newReplicationHandler(log *zap.Logger, svc ReplicationService) *ReplicationHandler { 96 h := &ReplicationHandler{ 97 log: log, 98 api: kithttp.NewAPI(kithttp.WithLog(log)), 99 replicationsService: svc, 100 } 101 102 r := chi.NewRouter() 103 r.Use( 104 middleware.Recoverer, 105 middleware.RequestID, 106 middleware.RealIP, 107 ) 108 109 r.Route("/", func(r chi.Router) { 110 r.Get("/", h.handleGetReplications) 111 r.Post("/", h.handlePostReplication) 112 113 r.Route("/{id}", func(r chi.Router) { 114 r.Get("/", h.handleGetReplication) 115 r.Patch("/", h.handlePatchReplication) 116 r.Delete("/", h.handleDeleteReplication) 117 r.Post("/validate", h.handleValidateReplication) 118 }) 119 }) 120 121 h.Router = r 122 return h 123 } 124 125 func (h *ReplicationHandler) Prefix() string { 126 return prefixReplications 127 } 128 129 func (h *ReplicationHandler) handleGetReplications(w http.ResponseWriter, r *http.Request) { 130 q := r.URL.Query() 131 132 // orgID is required for listing replications. 133 orgID := q.Get("orgID") 134 o, err := platform.IDFromString(orgID) 135 if err != nil { 136 h.api.Err(w, r, errBadOrg) 137 return 138 } 139 140 // name, remoteID, and localBucketID are optional additional filters. 141 name := q.Get("name") 142 remoteID := q.Get("remoteID") 143 localBucketID := q.Get("localBucketID") 144 145 filters := influxdb.ReplicationListFilter{OrgID: *o} 146 if name != "" { 147 filters.Name = &name 148 } 149 if remoteID != "" { 150 i, err := platform.IDFromString(remoteID) 151 if err != nil { 152 h.api.Err(w, r, errBadRemoteID) 153 return 154 } 155 filters.RemoteID = i 156 } 157 if localBucketID != "" { 158 i, err := platform.IDFromString(localBucketID) 159 if err != nil { 160 h.api.Err(w, r, errBadLocalBucketID) 161 return 162 } 163 filters.LocalBucketID = i 164 } 165 166 rs, err := h.replicationsService.ListReplications(r.Context(), filters) 167 if err != nil { 168 h.api.Err(w, r, err) 169 return 170 } 171 h.api.Respond(w, r, http.StatusOK, rs) 172 } 173 174 func (h *ReplicationHandler) handlePostReplication(w http.ResponseWriter, r *http.Request) { 175 ctx := r.Context() 176 q := r.URL.Query() 177 178 validate := q.Get("validate") == "true" 179 req := influxdb.CreateReplicationRequest{MaxQueueSizeBytes: influxdb.DefaultReplicationMaxQueueSizeBytes} 180 if err := h.api.DecodeJSON(r.Body, &req); err != nil { 181 h.api.Err(w, r, err) 182 return 183 } 184 185 if validate { 186 if err := h.replicationsService.ValidateNewReplication(ctx, req); err != nil { 187 h.api.Err(w, r, err) 188 return 189 } 190 h.api.Respond(w, r, http.StatusNoContent, nil) 191 return 192 } 193 194 replication, err := h.replicationsService.CreateReplication(ctx, req) 195 if err != nil { 196 h.api.Err(w, r, err) 197 return 198 } 199 h.api.Respond(w, r, http.StatusCreated, replication) 200 } 201 202 func (h *ReplicationHandler) handleGetReplication(w http.ResponseWriter, r *http.Request) { 203 id, err := platform.IDFromString(chi.URLParam(r, "id")) 204 if err != nil { 205 h.api.Err(w, r, errBadId) 206 return 207 } 208 209 replication, err := h.replicationsService.GetReplication(r.Context(), *id) 210 if err != nil { 211 h.api.Err(w, r, err) 212 return 213 } 214 h.api.Respond(w, r, http.StatusOK, replication) 215 } 216 217 func (h *ReplicationHandler) handlePatchReplication(w http.ResponseWriter, r *http.Request) { 218 id, err := platform.IDFromString(chi.URLParam(r, "id")) 219 if err != nil { 220 h.api.Err(w, r, errBadId) 221 return 222 } 223 224 ctx := r.Context() 225 q := r.URL.Query() 226 227 validate := q.Get("validate") == "true" 228 var req influxdb.UpdateReplicationRequest 229 if err := h.api.DecodeJSON(r.Body, &req); err != nil { 230 h.api.Err(w, r, err) 231 return 232 } 233 234 if validate { 235 if err := h.replicationsService.ValidateUpdatedReplication(ctx, *id, req); err != nil { 236 h.api.Err(w, r, err) 237 return 238 } 239 h.api.Respond(w, r, http.StatusNoContent, nil) 240 return 241 } 242 243 replication, err := h.replicationsService.UpdateReplication(ctx, *id, req) 244 if err != nil { 245 h.api.Err(w, r, err) 246 return 247 } 248 h.api.Respond(w, r, http.StatusOK, replication) 249 } 250 251 func (h *ReplicationHandler) handleDeleteReplication(w http.ResponseWriter, r *http.Request) { 252 id, err := platform.IDFromString(chi.URLParam(r, "id")) 253 if err != nil { 254 h.api.Err(w, r, errBadId) 255 return 256 } 257 258 if err := h.replicationsService.DeleteReplication(r.Context(), *id); err != nil { 259 h.api.Err(w, r, err) 260 return 261 } 262 h.api.Respond(w, r, http.StatusNoContent, nil) 263 } 264 265 func (h *ReplicationHandler) handleValidateReplication(w http.ResponseWriter, r *http.Request) { 266 id, err := platform.IDFromString(chi.URLParam(r, "id")) 267 if err != nil { 268 h.api.Err(w, r, errBadId) 269 return 270 } 271 272 if err := h.replicationsService.ValidateReplication(r.Context(), *id); err != nil { 273 h.api.Err(w, r, err) 274 return 275 } 276 h.api.Respond(w, r, http.StatusNoContent, nil) 277 }