func (rh *RootHandler) serveDiscovery(rw http.ResponseWriter, req *http.Request) { d := &camtypes.Discovery{ BlobRoot: rh.BlobRoot, JSONSignRoot: rh.JSONSignRoot, HelpRoot: rh.helpRoot, ImporterRoot: rh.importerRoot, SearchRoot: rh.SearchRoot, StatusRoot: rh.statusRoot, OwnerName: rh.OwnerName, UserName: rh.Username, AuthToken: auth.Token(), ThumbVersion: images.ThumbnailVersion(), } if gener, ok := rh.Storage.(blobserver.Generationer); ok { initTime, gen, err := gener.StorageGeneration() if err != nil { d.StorageGenerationError = err.Error() } else { d.StorageInitTime = types.Time3339(initTime) d.StorageGeneration = gen } } else { log.Printf("Storage type %T is not a blobserver.Generationer; not sending storageGeneration", rh.Storage) } if rh.ui != nil { d.UIDiscovery = rh.ui.discovery() } if rh.sigh != nil { d.Signing = rh.sigh.Discovery(rh.JSONSignRoot) } if len(rh.sync) > 0 { syncHandlers := make([]camtypes.SyncHandlerDiscovery, 0, len(rh.sync)) for _, sh := range rh.sync { syncHandlers = append(syncHandlers, sh.discovery()) } d.SyncHandlers = syncHandlers } discoveryHelper(rw, req, d) }
func (sh *SyncHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if req.Method == "POST" { if req.FormValue("mode") == "validate" { token := req.FormValue("token") if xsrftoken.Valid(token, auth.Token(), "user", "runFullValidate") { sh.startFullValidation() http.Redirect(rw, req, "./", http.StatusFound) return } } http.Error(rw, "Bad POST request", http.StatusBadRequest) return } // TODO: remove this lock and instead just call currentStatus, // and transition to using that here. sh.mu.Lock() defer sh.mu.Unlock() f := func(p string, a ...interface{}) { fmt.Fprintf(rw, p, a...) } now := time.Now() f("<h1>Sync Status (for %s to %s)</h1>", sh.fromName, sh.toName) f("<p><b>Current status: </b>%s</p>", html.EscapeString(sh.status)) if sh.idle { return } f("<h2>Stats:</h2><ul>") f("<li>Source: %s</li>", html.EscapeString(storageDesc(sh.from))) f("<li>Target: %s</li>", html.EscapeString(storageDesc(sh.to))) f("<li>Blobs synced: %d</li>", sh.totalCopies) f("<li>Bytes synced: %d</li>", sh.totalCopyBytes) f("<li>Blobs yet to copy: %d</li>", len(sh.needCopy)) f("<li>Bytes yet to copy: %d</li>", sh.bytesRemain) if !sh.recentCopyTime.IsZero() { f("<li>Most recent copy: %s (%v ago)</li>", sh.recentCopyTime.Format(time.RFC3339), now.Sub(sh.recentCopyTime)) } clarification := "" if len(sh.needCopy) == 0 && sh.totalErrors > 0 { clarification = "(all since resolved)" } f("<li>Previous copy errors: %d %s</li>", sh.totalErrors, clarification) f("</ul>") f("<h2>Validation</h2>") if len(sh.vshards) == 0 { f("Validation disabled") token := xsrftoken.Generate(auth.Token(), "user", "runFullValidate") f("<form method='POST'><input type='hidden' name='mode' value='validate'><input type='hidden' name='token' value='%s'><input type='submit' value='Start validation'></form>", token) } else { f("<p>Background scan of source and destination to ensure that the destination has everything the source does, or is at least enqueued to sync.</p>") f("<ul>") f("<li>Shards complete: %d/%d (%.1f%%)</li>", sh.vshardDone, len(sh.vshards), 100*float64(sh.vshardDone)/float64(len(sh.vshards))) f("<li>Source blobs seen: %d</li>", sh.vsrcCount) f("<li>Source bytes seen: %d</li>", sh.vsrcBytes) f("<li>Dest blobs seen: %d</li>", sh.vdestCount) f("<li>Dest bytes seen: %d</li>", sh.vdestBytes) f("<li>Blobs found missing & enqueued: %d</li>", sh.vmissing) if len(sh.vshardErrs) > 0 { f("<li>Validation errors:<ul>\n") for _, e := range sh.vshardErrs { f(" <li>%s</li>\n", html.EscapeString(e)) } f("</li>\n") } f("</ul>") } if len(sh.copying) > 0 { f("<h2>Currently Copying</h2><ul>") copying := make([]blob.Ref, 0, len(sh.copying)) for br := range sh.copying { copying = append(copying, br) } sort.Sort(blob.ByRef(copying)) for _, br := range copying { f("<li>%s</li>\n", sh.copying[br]) } f("</ul>") } recentErrors := make([]blob.Ref, 0, len(sh.recentErrors)) for _, br := range sh.recentErrors { if _, ok := sh.needCopy[br]; ok { // Only show it in the web UI if it's still a problem. Blobs that // have since succeeded just confused people. recentErrors = append(recentErrors, br) } } if len(recentErrors) > 0 { f("<h2>Recent Errors</h2><p>Blobs that haven't successfully copied over yet, and their last errors:</p><ul>") for _, br := range recentErrors { fail := sh.lastFail[br] f("<li>%s: %s: %s</li>\n", br, fail.when.Format(time.RFC3339), html.EscapeString(fail.err.Error())) } f("</ul>") } }