diff --git a/godoc/dl/dl.go b/godoc/dl/dl.go index ec0f4c3b..4e41212b 100644 --- a/godoc/dl/dl.go +++ b/godoc/dl/dl.go @@ -21,6 +21,7 @@ import ( "sort" "strconv" "strings" + "sync" "time" "golang.org/x/net/context" @@ -30,7 +31,6 @@ import ( "google.golang.org/appengine/log" "google.golang.org/appengine/memcache" "google.golang.org/appengine/user" - "google.golang.org/cloud/compute/metadata" ) const ( @@ -39,12 +39,6 @@ const ( cacheDuration = time.Hour ) -var builderKey string - -func init() { - builderKey, _ = metadata.ProjectAttributeValue("builder-key") -} - func RegisterHandlers(mux *http.ServeMux) { mux.Handle("/dl", http.RedirectHandler("/dl/", http.StatusFound)) mux.HandleFunc("/dl/", getHandler) // also serves listHandler @@ -336,10 +330,6 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "bad user", http.StatusForbidden) return } - if builderKey == "" { - http.Error(w, "no builder-key found in project metadata", http.StatusInternalServerError) - return - } if r.FormValue("key") != userKey(c, user) { http.Error(w, "bad key", http.StatusForbidden) return @@ -393,7 +383,7 @@ func validUser(user string) bool { } func userKey(c context.Context, user string) string { - h := hmac.New(md5.New, []byte(builderKey)) + h := hmac.New(md5.New, []byte(secret(c))) h.Write([]byte("user-" + user)) return fmt.Sprintf("%x", h.Sum(nil)) } @@ -448,3 +438,55 @@ var prettyStrings = map[string]string{ "installer": "Installer", "source": "Source", } + +// Code below copied from x/build/app/key + +var theKey struct { + sync.RWMutex + builderKey +} + +type builderKey struct { + Secret string +} + +func (k *builderKey) Key(c context.Context) *datastore.Key { + return datastore.NewKey(c, "BuilderKey", "root", 0, nil) +} + +func secret(c context.Context) string { + // check with rlock + theKey.RLock() + k := theKey.Secret + theKey.RUnlock() + if k != "" { + return k + } + + // prepare to fill; check with lock and keep lock + theKey.Lock() + defer theKey.Unlock() + if theKey.Secret != "" { + return theKey.Secret + } + + // fill + if err := datastore.Get(c, theKey.Key(c), &theKey.builderKey); err != nil { + if err == datastore.ErrNoSuchEntity { + // If the key is not stored in datastore, write it. + // This only happens at the beginning of a new deployment. + // The code is left here for SDK use and in case a fresh + // deployment is ever needed. "gophers rule" is not the + // real key. + if !appengine.IsDevAppServer() { + panic("lost key from datastore") + } + theKey.Secret = "gophers rule" + datastore.Put(c, theKey.Key(c), &theKey.builderKey) + return theKey.Secret + } + panic("cannot load builder key: " + err.Error()) + } + + return theKey.Secret +}