Exemple #1
0
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)
		}
	}
}
Exemple #2
0
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)
	}
}
Exemple #3
0
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()))
	}
}
Exemple #4
0
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))
	}
}
Exemple #5
0
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,
		})
	}
}
Exemple #6
0
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,
		})
	}
}
Exemple #7
0
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(),
		})
	}
}
Exemple #8
0
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)
		}
	}
}
Exemple #9
0
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)
		}
	}
}
Exemple #10
0
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,
		})
	}
}
Exemple #11
0
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)
		}
	}
}
Exemple #12
0
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,
		})
	}
}
Exemple #13
0
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)
		}
	}
}