devapp: put appengine code behind build flag

Per discussion on the mailing list we want to make this app run
on more environments than App Engine and hopefully with a backing
datastore that is just in-memory, or at the very least is not App
Engine specific.

Puts the datastore implementation detail behind an appengine build flag.
Adds an in-memory datastore if you are not running on App Engine; it's slow
since you have to fetch issues every time, but you can get all of the issues
and browse them. Adds a single test so we have the most basic of protections
against a regression.

Add a cmd/devapp main package so we can run the server outside of an
App Engine context.

Rename gg.Percentile to gg.Quantile to match the change in the latest
version of the downstream library.

Change-Id: Icbdef29676ecbf7078b0fb8c3920f61df60a5e2e
Reviewed-on: https://go-review.googlesource.com/34928
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/devapp/cache.go b/devapp/cache.go
index dfbd5d5..2bfd49a 100644
--- a/devapp/cache.go
+++ b/devapp/cache.go
@@ -10,8 +10,6 @@
 	"encoding/gob"
 
 	"golang.org/x/net/context"
-	"google.golang.org/appengine/datastore"
-	"google.golang.org/appengine/log"
 )
 
 // Cache is a datastore entity type that contains serialized data for dashboards.
@@ -22,27 +20,6 @@
 	Value []byte
 }
 
-func getCaches(ctx context.Context, names ...string) map[string]*Cache {
-	out := make(map[string]*Cache)
-	var keys []*datastore.Key
-	var ptrs []*Cache
-	for _, name := range names {
-		keys = append(keys, datastore.NewKey(ctx, entityPrefix+"Cache", name, 0, nil))
-		out[name] = &Cache{}
-		ptrs = append(ptrs, out[name])
-	}
-	datastore.GetMulti(ctx, keys, ptrs) // Ignore errors since they might not exist.
-	return out
-}
-
-func getCache(ctx context.Context, name string) (*Cache, error) {
-	var cache Cache
-	if err := datastore.Get(ctx, datastore.NewKey(ctx, entityPrefix+"Cache", name, 0, nil), &cache); err != nil {
-		return nil, err
-	}
-	return &cache, nil
-}
-
 func unpackCache(cache *Cache, data interface{}) error {
 	if len(cache.Value) > 0 {
 		gzr, err := gzip.NewReader(bytes.NewReader(cache.Value))
@@ -78,8 +55,5 @@
 	}
 	cache.Value = cacheout.Bytes()
 	log.Infof(ctx, "Cache %q update finished; writing %d bytes", name, cacheout.Len())
-	if _, err := datastore.Put(ctx, datastore.NewKey(ctx, entityPrefix+"Cache", name, 0, nil), &cache); err != nil {
-		return err
-	}
-	return nil
+	return putCache(ctx, name, &cache)
 }