| // Copyright 2017 syzkaller project authors. All rights reserved. |
| // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. |
| |
| package dash |
| |
| import ( |
| "fmt" |
| "net/http" |
| |
| "golang.org/x/net/context" |
| "google.golang.org/appengine/datastore" |
| "google.golang.org/appengine/log" |
| ) |
| |
| // dropNamespace drops all entities related to a single namespace. |
| // Use with care. There is no undo. |
| // This functionality is intentionally not connected to any handler. |
| // To use it, first make a backup of the datastore. Then, specify the target |
| // namespace in the ns variable, connect the function to a handler, invoke it |
| // and double check the output. Finally, set dryRun to false and invoke again. |
| func dropNamespace(c context.Context, w http.ResponseWriter, r *http.Request) error { |
| ns := "non-existent" |
| dryRun := true |
| if !dryRun { |
| log.Criticalf(c, "dropping namespace %v", ns) |
| } |
| w.Header().Set("Content-Type", "text/plain; charset=utf-8") |
| fmt.Fprintf(w, "dropping namespace %v\n", ns) |
| if err := dropNamespaceReportingState(c, w, ns, dryRun); err != nil { |
| return err |
| } |
| type Entity struct { |
| name string |
| child string |
| } |
| entities := []Entity{ |
| {textPatch, ""}, |
| {textReproC, ""}, |
| {textReproSyz, ""}, |
| {textKernelConfig, ""}, |
| {"Job", ""}, |
| {textError, ""}, |
| {textCrashLog, ""}, |
| {textCrashReport, ""}, |
| {"Build", ""}, |
| {"Manager", "ManagerStats"}, |
| {"Bug", "Crash"}, |
| } |
| for _, entity := range entities { |
| keys, err := datastore.NewQuery(entity.name). |
| Filter("Namespace=", ns). |
| KeysOnly(). |
| GetAll(c, nil) |
| if err != nil { |
| return err |
| } |
| fmt.Fprintf(w, "%v: %v\n", entity.name, len(keys)) |
| if entity.child != "" { |
| var childKeys []*datastore.Key |
| for _, key := range keys { |
| keys1, err := datastore.NewQuery(entity.child). |
| Ancestor(key). |
| KeysOnly(). |
| GetAll(c, nil) |
| if err != nil { |
| return err |
| } |
| childKeys = append(childKeys, keys1...) |
| } |
| fmt.Fprintf(w, " %v: %v\n", entity.child, len(childKeys)) |
| if err := dropEntities(c, childKeys, dryRun); err != nil { |
| return err |
| } |
| } |
| if err := dropEntities(c, keys, dryRun); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| var _ = dropNamespace // prevent warnings about dead code |
| |
| func dropNamespaceReportingState(c context.Context, w http.ResponseWriter, ns string, dryRun bool) error { |
| tx := func(c context.Context) error { |
| state, err := loadReportingState(c) |
| if err != nil { |
| return err |
| } |
| newState := new(ReportingState) |
| for _, ent := range state.Entries { |
| if ent.Namespace != ns { |
| newState.Entries = append(newState.Entries, ent) |
| } |
| } |
| if !dryRun { |
| if err := saveReportingState(c, newState); err != nil { |
| return err |
| } |
| } |
| fmt.Fprintf(w, "ReportingState: %v\n", len(state.Entries)-len(newState.Entries)) |
| return nil |
| } |
| return datastore.RunInTransaction(c, tx, nil) |
| } |
| |
| func dropEntities(c context.Context, keys []*datastore.Key, dryRun bool) error { |
| if dryRun { |
| return nil |
| } |
| for len(keys) != 0 { |
| batch := 100 |
| if batch > len(keys) { |
| batch = len(keys) |
| } |
| if err := datastore.DeleteMulti(c, keys[:batch]); err != nil { |
| return err |
| } |
| keys = keys[batch:] |
| } |
| return nil |
| } |