Documentation
¶
Overview ¶
Package actions implements a log of actions in the database. An action is anything that affects the outside world, such as edits to GitHub or Gerrit.
The action log uses database keys beginning with "action.Log" and an "action kind" string that describes the rest of the key and the format of the action and its result. The caller provides the rest of the key. For example, GitHub issue keys look like
["action.Log", "githubIssue", project, issue]
Action log values are described by the Entry type. Values include the parts of the key, an encoded action that provides enough information to perform it, and the result of the action after it is carried out. There are also fields for approval, discussed below.
Components that wish to use the action log must call Register to install a unique action kind along with a function that will run the action. Register returns a "before function" to call to add the action to the log before it is run. By choosing a key for the action that corresponds to an activity that it wishes to perform only once, a component can avoid duplicate executions of an action. For example, if a component wants to edit a GitHub comment only once, the key can be the URL for that comment. The before function will not write an action to the log if its key is already present. It returns false in this case, but the component is free to ignore this value.
Once it has called the before function (typically in its Run method), the component has nothing more to do at that time. At some later time, this package's Run function will be called to execute all pending actions (those added to the log but not yet run). Run will use the action kind of the pending action to dispatch to the registered run function, returning control to the component that logged the action.
Approvals ¶
Some actions require approval before they can be executed. [Entry.ApprovalRequired] represents that, and [Entry.Decisions] records whether the action was approved or denied, by whom, and when. An action may be approved or denied multiple times. Approval is denied if there is at least one denial.
Other DB entries ¶
This package stores other relationships in the database besides the log entries.
Keys beginning with "action.Pending" store the list of pending actions. The rest of the key is the action's key, and the value is nil. Actions are added to the pending list when they are first logged, and removed when they have been approved (if needed) and executed. (We cannot use a timed.Watcher for this purpose, because approvals can happen out of order.)
Keys beginning with "action.Wallclock" map wall clock times (time.Time values) to DBTimes. The mapping facilitates common log queries, like "show me the last hour of logs." The keys have the form
["action.Wallclock", time.Time.UnixNanos, DBTime]
The values are nil. Storing both times in the key permits multiple DBTimes for the same wall clock time.
Index ¶
- Constants
- func AddDecision(db storage.DB, actionKind string, key []byte, d Decision)
- func ClearLogForTesting(_ *testing.T, db storage.DB)
- func ReRunAction(ctx context.Context, lg *slog.Logger, db storage.DB, actionKind string, ...) (err error)
- func Run(ctx context.Context, lg *slog.Logger, db storage.DB) error
- func Scan(db storage.DB, start, end []byte) iter.Seq[*Entry]
- func ScanAfter(lg *slog.Logger, db storage.DB, t time.Time, ...) iter.Seq[*Entry]
- func ScanAfterDBTime(lg *slog.Logger, db storage.DB, t timed.DBTime, ...) iter.Seq[*Entry]
- type Actioner
- type BeforeFunc
- type Decision
- type Entry
- type RunReport
Constants ¶
const RequiresApproval = true
RequiresApproval can be passed as the last argument to a BeforeFunc for clarity.
Variables ¶
This section is empty.
Functions ¶
func AddDecision ¶
AddDecision adds a Decision to the action referred to by actionKind, key and u. It panics if the action does not exist or does not require approval.
func ClearLogForTesting ¶
ClearLogForTesting deletes the entire action log. It is intended only for tests.
func ReRunAction ¶
func ReRunAction(ctx context.Context, lg *slog.Logger, db storage.DB, actionKind string, key []byte) (err error)
ReRunAction attempts to re-run a single action denoted by the given kind and key. Only actions that have previously failed can be re-run.
func Run ¶
Run runs all actions that are ready to run, in the order they were added. An action is ready to run if it is approved and has not already run. Run returns the errors of all failed actions.
func Scan ¶
Scan returns an iterator over action log entries with start ≤ key ≤ end. Keys begin with the actionKind string, followed by the key provided to [Before], followed by the uint64 returned by Before.
func ScanAfter ¶
func ScanAfter(lg *slog.Logger, db storage.DB, t time.Time, filter func(actionKind string, key []byte) bool) iter.Seq[*Entry]
ScanAfter returns an iterator over action log entries that were started after time t. If filter is non-nil, ScanAfter omits entries for which filter(actionKind, key) returns false.
func ScanAfterDBTime ¶
func ScanAfterDBTime(lg *slog.Logger, db storage.DB, t timed.DBTime, filter func(actionKind string, key []byte) bool) iter.Seq[*Entry]
ScanAfterDBTime returns an iterator over action log entries that were started after DBTime t. If filter is non-nil, ScanAfterDBTime omits entries for which filter(actionKind, key) returns false.
Types ¶
type Actioner ¶
type Actioner interface { // Run deserializes the action, executes it, then return // the serialized result and error. Run(context.Context, []byte) ([]byte, error) // ForDisplay returns a string describing the action in a way that is suitable // for display on web pages and by command-line tools. // The action is provided in serialized form, as with Run. ForDisplay([]byte) string }
An Actioner works with actions. Actioners are registered with Register
type BeforeFunc ¶
BeforeFunc is the type of functions that are called to log an action before it is run. It writes an entry to db's action log with the given key and a representation of the action. The key must be created with ordered.Encode. The action should be JSON-encoded so tools can process it.
The function reports whether the action was added to the DB, or is a duplicate (has the same key) of an action that is already in the log.
func Register ¶
func Register(actionKind string, a Actioner) BeforeFunc
Register associates the given action kind and Actioner. Only Actioner may be registered for each kind, except during testing, when Register always registers its arguments.
Register returns a function that should be called to log an action before it is run.
type Decision ¶
type Decision struct { Name string // name of person or system making the decision Time time.Time // time of the decision Approved bool // true if approved, false if denied }
A Decision describes the approval or denial of an action.
type Entry ¶
type Entry struct { Created time.Time // time of the Before call Kind string // determines the format of Key, Action and Result Key []byte // user-provided part of the key; arg to Before and After ModTime timed.DBTime // set by Get and ScanAfter, used to resume scan Action []byte // encoded action // Fields set by After Done time.Time // time of the After call, or 0 if not called Result []byte // encoded result Error string // error from attempted action, "" on success // Fields for approval ApprovalRequired bool Decisions []Decision // approval decisions }
An Entry is one entry in the action log.
func Get ¶
Get looks up the Entry associated with the given arguments. If there is no entry for key in the database, Get returns nil, false. Otherwise it returns the entry and true.
func (*Entry) ActionForDisplay ¶
ActionForDisplay looks up the action associated with e and calls [Actioner.StringForDisplay] on it.