blob: fcc48b6ce9211336e7cddd6882dfa07e7f4236cc [file] [log] [blame]
// 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"
db "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 db. 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", ""},
{textLog, ""},
{textError, ""},
{textCrashLog, ""},
{textCrashReport, ""},
{"Build", ""},
{"Manager", "ManagerStats"},
{"Bug", "Crash"},
}
for _, entity := range entities {
keys, err := db.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 []*db.Key
for _, key := range keys {
keys1, err := db.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 db.RunInTransaction(c, tx, nil)
}
func dropEntities(c context.Context, keys []*db.Key, dryRun bool) error {
if dryRun {
return nil
}
for len(keys) != 0 {
batch := 100
if batch > len(keys) {
batch = len(keys)
}
if err := db.DeleteMulti(c, keys[:batch]); err != nil {
return err
}
keys = keys[batch:]
}
return nil
}
// updateBugReporting adds missing reporting stages to bugs in a single namespace.
// Use with care. There is no undo.
// This can be used to migrate datastore to a new config with more reporting stages.
// This functionality is intentionally not connected to any handler.
// Before invoking it is recommended to stop all connected instances just in case.
func updateBugReporting(c context.Context, w http.ResponseWriter, r *http.Request) error {
if accessLevel(c, r) != AccessAdmin {
return fmt.Errorf("admin only")
}
ns := r.FormValue("ns")
if ns == "" {
return fmt.Errorf("no ns parameter")
}
var bugs []*Bug
keys, err := db.NewQuery("Bug").
Filter("Namespace=", ns).
GetAll(c, &bugs)
if err != nil {
return err
}
log.Warningf(c, "fetched %v bugs for namespce %v", len(bugs), ns)
cfg := config.Namespaces[ns]
var batchKeys []*db.Key
const batchSize = 20
for i, bug := range bugs {
if len(bug.Reporting) >= len(cfg.Reporting) {
continue
}
batchKeys = append(batchKeys, keys[i])
if len(batchKeys) == batchSize {
if err := updateBugReportingBatch(c, cfg, batchKeys); err != nil {
return err
}
batchKeys = nil
}
}
if len(batchKeys) != 0 {
if err := updateBugReportingBatch(c, cfg, batchKeys); err != nil {
return err
}
}
return nil
}
func updateBugReportingBatch(c context.Context, cfg *Config, keys []*db.Key) error {
tx := func(c context.Context) error {
bugs := make([]*Bug, len(keys))
if err := db.GetMulti(c, keys, bugs); err != nil {
return err
}
for _, bug := range bugs {
createBugReporting(bug, cfg)
}
_, err := db.PutMulti(c, keys, bugs)
return err
}
err := db.RunInTransaction(c, tx, &db.TransactionOptions{XG: true})
log.Warningf(c, "updated %v bugs: %v", len(keys), err)
return err
}
// Prevent warnings about dead code.
var (
_ = dropNamespace
_ = updateBugReporting
)