From f5e771b2001534be1ba5066cbeefc8e6b77e2739 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 4 Dec 2017 09:00:36 +0100 Subject: dashboard/app: add drop namespace functionality 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. --- dashboard/app/admin.go | 120 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 dashboard/app/admin.go diff --git a/dashboard/app/admin.go b/dashboard/app/admin.go new file mode 100644 index 000000000..74b9a4591 --- /dev/null +++ b/dashboard/app/admin.go @@ -0,0 +1,120 @@ +// 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{ + {"Patch", ""}, + {"ReproC", ""}, + {"ReproSyz", ""}, + {"KernelConfig", ""}, + {"Job", ""}, + {"Error", ""}, + {"CrashLog", ""}, + {"CrashReport", ""}, + {"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 +} + +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 +} -- cgit mrf-deployment