cmd/tailscale, ipn/ipnlocal: add debug command to write to StateStore for dev

Not for end users (unless directed by support). Mostly for ease of
development for some upcoming webserver work.

Change-Id: I43acfed217514567acb3312367b24d620e739f88
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/6223/head
Brad Fitzpatrick 2 years ago committed by Brad Fitzpatrick
parent bf2d3cd074
commit 5bb7e0307c

@ -348,6 +348,19 @@ func (lc *LocalClient) DebugAction(ctx context.Context, action string) error {
return nil
}
// SetDevStoreKeyValue set a statestore key/value. It's only meant for development.
// The schema (including when keys are re-read) is not a stable interface.
func (lc *LocalClient) SetDevStoreKeyValue(ctx context.Context, key, value string) error {
body, err := lc.send(ctx, "POST", "/localapi/v0/dev-set-state-store?"+(url.Values{
"key": {key},
"value": {value},
}).Encode(), 200, nil)
if err != nil {
return fmt.Errorf("error %w: %s", err, body)
}
return nil
}
// SetComponentDebugLogging sets component's debug logging enabled for
// the provided duration. If the duration is in the past, the debug logging
// is disabled.

@ -144,6 +144,16 @@ var debugCmd = &ffcli.Command{
return fs
})(),
},
{
Name: "dev-store-set",
Exec: runDevStoreSet,
ShortHelp: "set a key/value pair during development",
FlagSet: (func() *flag.FlagSet {
fs := newFlagSet("store-set")
fs.BoolVar(&devStoreSetArgs.danger, "danger", false, "accept danger")
return fs
})(),
},
},
}
@ -546,3 +556,17 @@ func runDebugComponentLogs(ctx context.Context, args []string) error {
}
return nil
}
var devStoreSetArgs struct {
danger bool
}
func runDevStoreSet(ctx context.Context, args []string) error {
if len(args) != 2 {
return errors.New("usage: dev-store-set --danger <key> <value>")
}
if !devStoreSetArgs.danger {
return errors.New("this command is dangerous; use --danger to proceed")
}
return localClient.SetDevStoreKeyValue(ctx, args[0], args[1])
}

@ -3966,3 +3966,15 @@ func (b *LocalBackend) Doctor(ctx context.Context, logf logger.Logf) {
doctor.RunChecks(ctx, logf, checks...)
}
// SetDevStateStore updates the LocalBackend's state storage to the provided values.
//
// It's meant only for development.
func (b *LocalBackend) SetDevStateStore(key, value string) error {
if b.store == nil {
return errors.New("no state store")
}
err := b.store.WriteState(ipn.StateKey(key), []byte(value))
b.logf("SetDevStateStore(%q, %q) = %v", key, value, err)
return err
}

@ -61,6 +61,7 @@ var handler = map[string]localAPIHandler{
"component-debug-logging": (*Handler).serveComponentDebugLogging,
"debug": (*Handler).serveDebug,
"derpmap": (*Handler).serveDERPMap,
"dev-set-state-store": (*Handler).serveDevSetStateStore,
"dial": (*Handler).serveDial,
"file-targets": (*Handler).serveFileTargets,
"goroutines": (*Handler).serveGoroutines,
@ -401,6 +402,23 @@ func (h *Handler) serveDebug(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "done\n")
}
func (h *Handler) serveDevSetStateStore(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "debug access denied", http.StatusForbidden)
return
}
if r.Method != "POST" {
http.Error(w, "POST required", http.StatusMethodNotAllowed)
return
}
if err := h.b.SetDevStateStore(r.FormValue("key"), r.FormValue("value")); err != nil {
http.Error(w, err.Error(), 500)
return
}
w.Header().Set("Content-Type", "text/plain")
io.WriteString(w, "done\n")
}
func (h *Handler) serveComponentDebugLogging(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "debug access denied", http.StatusForbidden)

Loading…
Cancel
Save