func vkvKeysHandler(db *vkv.DB) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": q := r.URL.Query() end := q.Get("end") if end == "" { end = "\xff" } limit := 0 if q.Get("limit") != "" { ilimit, err := strconv.Atoi(q.Get("limit")) if err != nil { http.Error(w, "bad limit", 500) } limit = ilimit } res, err := db.Keys(q.Get("start"), end, limit) if err != nil { panic(err) } httputil.WriteJSON(w, map[string]interface{}{"keys": res}) return default: w.WriteHeader(http.StatusMethodNotAllowed) } } }
func (st *SyncTable) triggerHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() vars := mux.Vars(r) ns := vars["ns"] log := st.log.New("trigger_id", logext.RandId(6), "ns", ns) url := q.Get("url") log.Info("Starting sync...", "url", url) apiKey := q.Get("api_key") rawState := st.generateTree(ns) defer rawState.Close() state := &State{ Namespace: ns, Root: rawState.Root(), Count: rawState.Count(), Leafs: rawState.Level1(), } client := NewSyncTableClient(state, st.blobstore, st.nsdb, ns, url, apiKey, st.blobs) stats, err := client.Sync() if err != nil { panic(err) } httputil.WriteJSON(w, stats) } }
func (docstore *DocStoreExt) docHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) collection := vars["collection"] if collection == "" { panic("missing collection query arg") } sid := vars["_id"] if sid == "" { panic("missing _id query arg") } var _id *id.ID var err error srw := httputil.NewSnappyResponseWriter(w, r) defer srw.Close() switch r.Method { case "GET": js := []byte{} if _id, err = docstore.fetchDoc(collection, sid, &js); err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") srw.Write(addID(js, sid)) case "POST": doc := map[string]interface{}{} if _id, err = docstore.fetchDoc(collection, sid, &doc); err != nil { panic(err) } var update map[string]interface{} decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&update); err != nil { panic(err) } docstore.logger.Debug("Update", "_id", sid, "update_query", update) newDoc, err := updateDoc(doc, update) blob, err := json.Marshal(newDoc) if err != nil { panic(err) } hash := fmt.Sprintf("%x", blake2b.Sum256(blob)) docstore.blobStore.Put(hash, blob) bash := make([]byte, len(hash)+1) // FIXME(tsileo) fetch previous flag and set the same bash[0] = FlagIndexed copy(bash[1:], []byte(hash)[:]) if _, err := docstore.kvStore.Put(fmt.Sprintf(KeyFmt, collection, _id.String()), string(bash), -1); err != nil { panic(err) } httputil.WriteJSON(srw, newDoc) } w.Header().Set("BlobStash-DocStore-Doc-Id", sid) w.Header().Set("BlobStash-DocStore-Doc-Hash", _id.Hash()) w.Header().Set("BlobStash-DocStore-Doc-CreatedAt", strconv.Itoa(_id.Ts())) } }
func (lua *LuaExt) AppStatsHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lua.appMutex.Lock() defer lua.appMutex.Unlock() app, ok := lua.registeredApps[r.URL.Query().Get("appID")] if !ok { panic("no such app") } httputil.WriteJSON(w, appToResp(app)) } }
func (lua *LuaExt) AppsHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lua.appMutex.Lock() defer lua.appMutex.Unlock() apps := []*LuaAppResp{} for _, app := range lua.registeredApps { apps = append(apps, appToResp(app)) } httputil.WriteJSON(w, map[string]interface{}{ "apps": apps, }) } }
func (lua *LuaExt) AppLogsHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { lua.appMutex.Lock() defer lua.appMutex.Unlock() app, ok := lua.registeredApps[r.URL.Query().Get("appID")] if !ok { panic("no such app") } httputil.WriteJSON(w, map[string]interface{}{ "logs": app.logs, }) } }
func (st *SyncTable) stateHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) ns := vars["ns"] st.log.Info("_state called", "ns", ns) state := st.generateTree(ns) defer state.Close() httputil.WriteJSON(w, map[string]interface{}{ "namespace": ns, "root": state.Root(), "count": state.Count(), "leafs": state.Level1(), }) } }
func vkvVersionsHandler(db *vkv.DB) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": // TODO handle start/end/limit vars := mux.Vars(r) res, err := db.Versions(vars["key"], 0, int(time.Now().UTC().UnixNano()), 0) if err != nil { panic(err) } httputil.WriteJSON(w, res) return default: w.WriteHeader(http.StatusMethodNotAllowed) } } }
func (docstore *DocStoreExt) collectionsHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": collections, err := docstore.Collections() if err != nil { panic(err) } srw := httputil.NewSnappyResponseWriter(w, r) httputil.WriteJSON(srw, map[string]interface{}{ "collections": collections, }) srw.Close() default: w.WriteHeader(http.StatusMethodNotAllowed) } } }
func (st *SyncTable) stateLeafsHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) ns := vars["ns"] prefix := vars["prefix"] hashes, err := st.nsdb.Namespace(ns, prefix) if err != nil { panic(err) } st.log.Info("_state/leafs called", "ns", ns, "prefix", prefix, "hashes", len(hashes)) httputil.WriteJSON(w, map[string]interface{}{ "namespace": ns, "prefix": prefix, "count": len(hashes), "hashes": hashes, }) } }
func blobsHandler(blobrouter *router.Router) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { req := &router.Request{ Type: router.Read, Namespace: r.URL.Query().Get("ns"), } backend := blobrouter.Route(req) switch r.Method { case "GET": // FIXME(tsileo): Re-implement this! //start := r.URL.Query().Get("start") //end := r.URL.Query().Get("end") //slimit := r.URL.Query().Get("limit") //limit := 0 //if slimit != "" { // lim, err := strconv.Atoi(slimit) // if err != nil { // panic(err) // } // limit = lim //} blobs := make(chan string) errc := make(chan error, 1) go func() { errc <- backend.Enumerate(blobs) }() res := []string{} for blob := range blobs { res = append(res, blob) } if err := <-errc; err != nil { panic(err) } httputil.WriteJSON(w, map[string]interface{}{"blobs": res}) default: w.WriteHeader(http.StatusMethodNotAllowed) } } }
func (st *SyncTable) syncHandler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(http.StatusMethodNotAllowed) return } vars := mux.Vars(r) ns := vars["ns"] log := st.log.New("sync_id", logext.RandId(6), "ns", ns) log.Info("sync triggered") state := st.generateTree(ns) defer state.Close() local_state := &State{ Namespace: ns, Root: state.Root(), Leafs: state.Level1(), Count: state.Count(), } log.Debug("local state computed", "local_state", local_state.String()) remote_state := &State{} if err := json.NewDecoder(r.Body).Decode(remote_state); err != nil { panic(err) } log.Debug("remote state decoded", "remote_state", remote_state.String()) // First check the root, if the root hash is the same, then we can't stop here, we are in sync. if local_state.Root == remote_state.Root { log.Debug("No sync needed") w.WriteHeader(http.StatusNoContent) return } // The root differs, found out the leafs we need to inspect leafsNeeded := []string{} leafsToSend := []string{} leafsConflict := []string{} for lleaf, lh := range local_state.Leafs { if rh, ok := remote_state.Leafs[lleaf]; ok { if lh != rh { leafsConflict = append(leafsConflict, lleaf) } } else { // This leaf is only present locally, we can send blindly all the blobs belonging to this leaf leafsToSend = append(leafsToSend, lleaf) // If an entire leaf is missing, this means we can send/receive the entire hashes for the missing leaf } } // Find out the leafs present only on the remote-side for rleaf, _ := range remote_state.Leafs { if _, ok := local_state.Leafs[rleaf]; !ok { leafsNeeded = append(leafsNeeded, rleaf) } } httputil.WriteJSON(w, map[string]interface{}{ "conflicted": leafsConflict, "needed": leafsNeeded, "missing": leafsToSend, }) } }
func vkvHandler(wg sync.WaitGroup, db *vkv.DB, kvUpdate chan *vkv.KeyValue, blobrouter *router.Router) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) switch r.Method { case "GET": iversion := -1 version := r.URL.Query().Get("version") if version != "" { iver, err := strconv.Atoi(version) if err != nil { panic(err) } iversion = iver } res, err := db.Get(vars["key"], iversion) if err != nil { if err == vkv.ErrNotFound { http.Error(w, http.StatusText(404), 404) return } panic(err) } httputil.WriteJSON(w, res) case "HEAD": exists, err := db.Check(vars["key"]) if err != nil { panic(err) } if exists { return } http.Error(w, http.StatusText(404), 404) return case "DELETE": k := vars["key"] sversion := r.URL.Query().Get("version") if sversion == "" { http.Error(w, "version missing", 500) return } version, err := strconv.Atoi(sversion) if err != nil { http.Error(w, "bad version", 500) return } hash, err := db.MetaBlob(k, version) if err != nil { panic(err) } if err := db.DeleteVersion(k, version); err != nil { panic(err) } req := &router.Request{ Type: router.Read, MetaBlob: true, } if err := blobrouter.Route(req).Delete(hash); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } case "PUT": wg.Add(1) defer wg.Done() k := vars["key"] hah, err := ioutil.ReadAll(r.Body) values, err := url.ParseQuery(string(hah)) if err != nil { panic(err) } v := values.Get("value") sversion := values.Get("version") version := -1 if sversion != "" { iversion, err := strconv.Atoi(sversion) if err != nil { http.Error(w, "bad version", 500) return } version = iversion } res, err := db.Put(k, v, version) // FIXME(tsileo): handle namespace in embeded client too res.SetNamespace(values.Get("ns")) if err != nil { panic(err) } kvUpdate <- res httputil.WriteJSON(w, res) default: w.WriteHeader(http.StatusMethodNotAllowed) } } }