lib

package
v0.19.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2018 License: AGPL-3.0 Imports: 22 Imported by: 0

Documentation

Overview

Package lib is a kitchen sink of... basically anything that doesn't belong in a specific part of the codebase, ranging from utility functions to universal types to core interfaces.

Some of the things in lib do not actually belong in lib, most notably the Executor and Runner, which arguably belong in core. Other things are in files that are far too long, or that do not actually make sense.

Feel free to move these things.

Index

Constants

View Source
const GroupSeparator = "::"

Separator for group IDs.

Variables

View Source
var ErrNameContainsGroupSeparator = errors.New("group and check names may not contain '::'")

Error emitted if you attempt to instantiate a Group or Check that contains the separator.

View Source
var SupportedTLSCipherSuites = map[string]uint16{
	"TLS_RSA_WITH_RC4_128_SHA":                tls.TLS_RSA_WITH_RC4_128_SHA,
	"TLS_RSA_WITH_3DES_EDE_CBC_SHA":           tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
	"TLS_RSA_WITH_AES_128_CBC_SHA":            tls.TLS_RSA_WITH_AES_128_CBC_SHA,
	"TLS_RSA_WITH_AES_256_CBC_SHA":            tls.TLS_RSA_WITH_AES_256_CBC_SHA,
	"TLS_RSA_WITH_AES_128_GCM_SHA256":         tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
	"TLS_RSA_WITH_AES_256_GCM_SHA384":         tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
	"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA":        tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
	"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA":    tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
	"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA":    tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
	"TLS_ECDHE_RSA_WITH_RC4_128_SHA":          tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
	"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":     tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
	"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA":      tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
	"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA":      tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
	"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256":   tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
	"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
	"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384":   tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
	"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
}

String-to-constant map of available TLS cipher suites.

View Source
var SupportedTLSCipherSuitesToString = map[uint16]string{
	tls.TLS_RSA_WITH_RC4_128_SHA:                "TLS_RSA_WITH_RC4_128_SHA",
	tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA:           "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
	tls.TLS_RSA_WITH_AES_128_CBC_SHA:            "TLS_RSA_WITH_AES_128_CBC_SHA",
	tls.TLS_RSA_WITH_AES_256_CBC_SHA:            "TLS_RSA_WITH_AES_256_CBC_SHA",
	tls.TLS_RSA_WITH_AES_128_GCM_SHA256:         "TLS_RSA_WITH_AES_128_GCM_SHA256",
	tls.TLS_RSA_WITH_AES_256_GCM_SHA384:         "TLS_RSA_WITH_AES_256_GCM_SHA384",
	tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:        "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
	tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:    "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
	tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:    "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
	tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA:          "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
	tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:     "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
	tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:      "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
	tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:      "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
	tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:   "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
	tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
	tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:   "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
	tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
}

Constant-to-string map of available TLS cipher suites.

View Source
var SupportedTLSVersions = map[string]TLSVersion{
	"ssl3.0": tls.VersionSSL30,
	"tls1.0": tls.VersionTLS10,
	"tls1.1": tls.VersionTLS11,
	"tls1.2": tls.VersionTLS12,
}

String-to-constant map of available TLS versions.

View Source
var SupportedTLSVersionsToString = map[TLSVersion]string{
	tls.VersionSSL30: "ssl3.0",
	tls.VersionTLS10: "tls1.0",
	tls.VersionTLS11: "tls1.1",
	tls.VersionTLS12: "tls1.2",
}

Constant-to-string map of available TLS versions.

Functions

func AnonymizePath added in v0.15.0

func AnonymizePath(path string) string

Anonymizes a file path, by scrubbing usernames from home directories.

func Clampf added in v0.5.0

func Clampf(val, min, max float64) float64

Clampf returns the given value, "clamped" to the range [min, max].

func Lerp

func Lerp(x, y int64, t float64) int64

Lerp is a linear interpolation between two values x and y, returning the value at the point t, where t is a fraction in the range [0.0 - 1.0].

func Max added in v0.17.0

func Max(a, b int64) int64

Returns the maximum value of a and b.

func Min added in v0.17.0

func Min(a, b int64) int64

Returns the minimum value of a and b.

func SplitKV added in v0.8.2

func SplitKV(s string) (key, value string)

Splits a string in the form "key=value".

Types

type Archive added in v0.15.0

type Archive struct {
	// The runner to use, eg. "js".
	Type string `json:"type"`

	// Options to use.
	Options Options `json:"options"`

	// Filename and contents of the main file being executed.
	Filename string `json:"filename"`
	Data     []byte `json:"-"`

	// Working directory for resolving relative paths.
	Pwd string `json:"pwd"`

	// Archived filesystem.
	Scripts map[string][]byte `json:"-"` // included scripts
	Files   map[string][]byte `json:"-"` // non-script resources
}

An Archive is a rollup of all resources and options needed to reproduce a test identically elsewhere.

func ReadArchive added in v0.15.0

func ReadArchive(in io.Reader) (*Archive, error)

Reads an archive created by Archive.Write from a reader.

func (*Archive) Write added in v0.15.0

func (arc *Archive) Write(out io.Writer) error

Write serialises the archive to a writer.

The format should be treated as opaque; currently it is simply a TAR rollup, but this may change. If it does change, ReadArchive must be able to handle all previous formats as well as the current one.

type Check

type Check struct {
	// Arbitrary name of the check.
	Name string `json:"name"`

	// A Check belongs to a Group, which may belong to other groups. The Path describes
	// the hierarchy of these groups, with the segments delimited by '::'.
	// As an example: a check "My Check" within a group "Inner" within a group "Outer"
	// would have a Path of "::Outer::Inner::My Check". The empty first item is the root group,
	// which is always named "".
	Group *Group `json:"-"`
	Path  string `json:"path"`

	// A check's ID is a hash of the Path. It is deterministic between different k6
	// instances of the same version, but should be treated as opaque - the hash function
	// or length may change.
	ID string `json:"id"`

	// Counters for how many times this check has passed and failed respectively.
	Passes int64 `json:"passes"`
	Fails  int64 `json:"fails"`
}

A Check stores a series of successful or failing tests against a value.

For more information, refer to the js/modules/k6.K6.Check() function.

func NewCheck

func NewCheck(name string, group *Group) (*Check, error)

Creates a new check with the given name and parent group. The group may not be nil.

type Collector added in v0.8.3

type Collector interface {
	// Init is called between the collector's creation and the call to Run().
	// You should do any lenghty setup here rather than in New.
	Init() error

	// Run is called in a goroutine and starts the collector. Should commit samples to the backend
	// at regular intervals and when the context is terminated.
	Run(ctx context.Context)

	// Collect receives a set of samples. This method is never called concurrently, and only while
	// the context for Run() is valid, but should defer as much work as possible to Run().
	Collect(samples []stats.Sample)

	// Optionally return a link that is shown to the user.
	Link() string
}

A Collector abstracts the process of funneling samples to an external storage backend, such as an InfluxDB instance.

type Duration added in v0.9.2

type Duration time.Duration

Duration is an alias for time.Duration that de/serialises to JSON as human-readable strings.

func (Duration) MarshalJSON added in v0.16.0

func (d Duration) MarshalJSON() ([]byte, error)

func (Duration) String added in v0.16.0

func (d Duration) String() string

func (*Duration) UnmarshalJSON added in v0.9.2

func (d *Duration) UnmarshalJSON(data []byte) error

func (*Duration) UnmarshalText added in v0.18.0

func (d *Duration) UnmarshalText(data []byte) error

type Executor added in v0.17.0

type Executor interface {
	// Run the Executor, funneling generated samples through the out channel.
	Run(ctx context.Context, out chan<- []stats.Sample) error
	// Is the executor currently running?
	IsRunning() bool

	// Returns the wrapped runner. May return nil if not applicable, eg. if we're remote
	// controlling a test running on another machine.
	GetRunner() Runner

	// Get and set the logger. This is propagated to the Runner.
	GetLogger() *log.Logger
	SetLogger(l *log.Logger)

	// Get and set the list of stages.
	GetStages() []Stage
	SetStages(s []Stage)

	// Get iterations executed so far, get and set how many to end the test after.
	GetIterations() int64
	GetEndIterations() null.Int
	SetEndIterations(i null.Int)

	// Get time elapsed so far, accounting for pauses, get and set at what point to end the test.
	GetTime() time.Duration
	GetEndTime() NullDuration
	SetEndTime(t NullDuration)

	// Check whether the test is paused, or pause it. A paused won't start any new iterations (but
	// will allow currently in progress ones to finish), and will not increment the value returned
	// by GetTime().
	IsPaused() bool
	SetPaused(paused bool)

	// Get and set the number of currently active VUs.
	// It is an error to try to set this higher than MaxVUs.
	GetVUs() int64
	SetVUs(vus int64) error

	// Get and set the number of allocated, available VUs.
	// Please note that initialising new VUs is a very expensive operation, and doing it during a
	// running test may skew metrics; if you're not sure how many you will need, it's generally
	// speaking better to preallocate too many than too few.
	GetVUsMax() int64
	SetVUsMax(max int64) error
}

An Executor is in charge of scheduling VUs created by a wrapped Runner, but decouples how you control a swarm of VUs from the details of how or even where they're scheduled.

The core/local executor schedules VUs on the local machine, but the same interface may be implemented to control a test running on a cluster or in the cloud.

type Group

type Group struct {
	// Arbitrary name of the group.
	Name string `json:"name"`

	// A group may belong to another group, which may belong to another group, etc. The Path
	// describes the hierarchy leading down to this group, with the segments delimited by '::'.
	// As an example: a group "Inner" inside a group named "Outer" would have a path of
	// "::Outer::Inner". The empty first item is the root group, which is always named "".
	Parent *Group `json:"-"`
	Path   string `json:"path"`

	// A group's ID is a hash of the Path. It is deterministic between different k6
	// instances of the same version, but should be treated as opaque - the hash function
	// or length may change.
	ID string `json:"id"`

	// Groups and checks that are children of this group.
	Groups map[string]*Group `json:"groups"`
	Checks map[string]*Check `json:"checks"`
	// contains filtered or unexported fields
}

A Group is an organisational block, that samples and checks may be tagged with.

For more information, refer to the js/modules/k6.K6.Group() function.

func NewGroup

func NewGroup(name string, parent *Group) (*Group, error)

Creates a new group with the given name and parent group.

The root group must be created with the name "" and parent set to nil; this is the only case where a nil parent or empty name is allowed.

func (*Group) Check

func (g *Group) Check(name string) (*Check, error)

Create a check belonging to this group. This is safe to call from multiple goroutines simultaneously.

func (*Group) Group

func (g *Group) Group(name string) (*Group, error)

Create a child group belonging to this group. This is safe to call from multiple goroutines simultaneously.

type NullDuration added in v0.16.0

type NullDuration struct {
	Duration
	Valid bool
}

NullDuration is a nullable Duration, in the same vein as the nullable types provided by package gopkg.in/guregu/null.v3.

func NullDurationFrom added in v0.16.0

func NullDurationFrom(d time.Duration) NullDuration

Creates a valid NullDuration from a time.Duration.

func SumStages added in v0.18.2

func SumStages(stages []Stage) (d NullDuration)

Returns the total sum of time taken by the given set of stages.

func (NullDuration) MarshalJSON added in v0.16.0

func (d NullDuration) MarshalJSON() ([]byte, error)

func (*NullDuration) UnmarshalJSON added in v0.16.0

func (d *NullDuration) UnmarshalJSON(data []byte) error

func (*NullDuration) UnmarshalText added in v0.18.0

func (d *NullDuration) UnmarshalText(data []byte) error

type Options

type Options struct {
	// Should the test start in a paused state?
	Paused null.Bool `json:"paused" envconfig:"paused"`

	// Initial values for VUs, max VUs, duration cap, iteration cap, and stages.
	// See the Runner or Executor interfaces for more information.
	VUs        null.Int     `json:"vus" envconfig:"vus"`
	VUsMax     null.Int     `json:"vusMax" envconfig:"vus_max"`
	Duration   NullDuration `json:"duration" envconfig:"duration"`
	Iterations null.Int     `json:"iterations" envconfig:"iterations"`
	Stages     []Stage      `json:"stages" envconfig:"stages"`

	// Limit HTTP requests per second.
	RPS null.Int `json:"rps" envconfig:"rps"`

	// How many HTTP redirects do we follow?
	MaxRedirects null.Int `json:"maxRedirects" envconfig:"max_redirects"`

	// Default User Agent string for HTTP requests.
	UserAgent null.String `json:"userAgent" envconfig:"user_agent"`

	// How many batch requests are allowed in parallel, in total and per host?
	Batch        null.Int `json:"batch" envconfig:"batch"`
	BatchPerHost null.Int `json:"batchPerHost" envconfig:"batch_per_host"`

	// Accept invalid or untrusted TLS certificates.
	InsecureSkipTLSVerify null.Bool `json:"insecureSkipTLSVerify" envconfig:"insecure_skip_tls_verify"`

	// Specify TLS versions and cipher suites, and present client certificates.
	TLSCipherSuites *TLSCipherSuites `json:"tlsCipherSuites" envconfig:"tls_cipher_suites"`
	TLSVersion      *TLSVersions     `json:"tlsVersion" envconfig:"tls_version"`
	TLSAuth         []*TLSAuth       `json:"tlsAuth" envconfig:"tlsauth"`

	// Throw warnings (eg. failed HTTP requests) as errors instead of simply logging them.
	Throw null.Bool `json:"throw" envconfig:"throw"`

	// Define thresholds; these take the form of 'metric=["snippet1", "snippet2"]'.
	// To create a threshold on a derived metric based on tag queries ("submetrics"), create a
	// metric on a nonexistent metric named 'real_metric{tagA:valueA,tagB:valueB}'.
	Thresholds map[string]stats.Thresholds `json:"thresholds" envconfig:"thresholds"`

	// Blacklist IP ranges that tests may not contact. Mainly useful in hosted setups.
	BlacklistIPs []*net.IPNet `json:"blacklistIPs" envconfig:"blacklist_ips"`

	// Do not reuse connections between VU iterations. This gives more realistic results (depending
	// on what you're looking for), but you need to raise various kernel limits or you'll get
	// errors about running out of file handles or sockets, or being unable to bind addresses.
	NoConnectionReuse null.Bool `json:"noConnectionReuse" envconfig:"no_connection_reuse"`

	// These values are for third party collectors' benefit.
	// Can't be set through env vars.
	External map[string]interface{} `json:"ext" ignored:"true"`

	// Summary trend stats for trend metrics (response times) in CLI output
	SummaryTrendStats []string `json:"SummaryTrendStats" envconfig:"summary_trend_stats"`
}

func (Options) Apply

func (o Options) Apply(opts Options) Options

Returns the result of overwriting any fields with any that are set on the argument.

Example:

a := Options{VUs: null.IntFrom(10), VUsMax: null.IntFrom(10)}
b := Options{VUs: null.IntFrom(5)}
a.Apply(b) // Options{VUs: null.IntFrom(5), VUsMax: null.IntFrom(10)}

type Runner

type Runner interface {
	// Creates an Archive of the runner. There should be a corresponding NewFromArchive() function
	// that will restore the runner from the archive.
	MakeArchive() *Archive

	// Spawns a new VU. It's fine to make this function rather heavy, if it means a performance
	// improvement at runtime. Remember, this is called once per VU and normally only at the start
	// of a test - RunOnce() may be called hundreds of thousands of times, and must be fast.
	NewVU() (VU, error)

	// Returns the default (root) Group.
	GetDefaultGroup() *Group

	// Get and set options. The initial value will be whatever the script specifies (for JS,
	// `export let options = {}`); cmd/run.go will mix this in with CLI-, config- and env-provided
	// values and write it back to the runner.
	GetOptions() Options
	SetOptions(opts Options)
}

A Runner is a factory for VUs. It should precompute as much as possible upon creation (parse ASTs, load files into memory, etc.), so that spawning VUs becomes as fast as possible. The Runner doesn't actually *do* anything in itself, the Executor is responsible for wrapping and scheduling these VUs for execution.

TODO: Rename this to something more obvious? This name made sense a very long time ago.

type RunnerFunc added in v0.5.0

type RunnerFunc func(ctx context.Context) ([]stats.Sample, error)

RunnerFunc wraps a function in a runner whose VUs will simply call that function.

func (RunnerFunc) GetDefaultGroup added in v0.5.0

func (fn RunnerFunc) GetDefaultGroup() *Group

func (RunnerFunc) GetOptions added in v0.5.0

func (fn RunnerFunc) GetOptions() Options

func (RunnerFunc) MakeArchive added in v0.15.0

func (fn RunnerFunc) MakeArchive() *Archive

func (RunnerFunc) NewVU added in v0.5.0

func (fn RunnerFunc) NewVU() (VU, error)

func (RunnerFunc) SetOptions added in v0.18.1

func (fn RunnerFunc) SetOptions(opts Options)

func (RunnerFunc) VU added in v0.11.0

func (fn RunnerFunc) VU() *RunnerFuncVU

type RunnerFuncVU added in v0.11.0

type RunnerFuncVU struct {
	Fn RunnerFunc
	ID int64
}

A VU spawned by a RunnerFunc.

func (*RunnerFuncVU) Reconfigure added in v0.11.0

func (fn *RunnerFuncVU) Reconfigure(id int64) error

func (RunnerFuncVU) RunOnce added in v0.11.0

func (fn RunnerFuncVU) RunOnce(ctx context.Context) ([]stats.Sample, error)

type SourceData added in v0.9.0

type SourceData struct {
	Data     []byte
	Filename string
}

Wraps a source file; data and filename.

type Stage

type Stage StageFields

A Stage defines a step in a test's timeline.

func (Stage) MarshalJSON added in v0.18.0

func (s Stage) MarshalJSON() ([]byte, error)

func (*Stage) UnmarshalJSON added in v0.9.2

func (s *Stage) UnmarshalJSON(b []byte) error

For some reason, implementing UnmarshalText makes encoding/json treat the type as a string.

func (*Stage) UnmarshalText added in v0.18.0

func (s *Stage) UnmarshalText(b []byte) error

type StageFields added in v0.18.0

type StageFields struct {
	// Duration of the stage.
	Duration NullDuration `json:"duration"`

	// If Valid, the VU count will be linearly interpolated towards this value.
	Target null.Int `json:"target"`
}

StageFields defines the fields used for a Stage; this is a dumb hack to make the JSON code cleaner. pls fix.

type TLSAuth added in v0.17.2

type TLSAuth struct {
	TLSAuthFields
	// contains filtered or unexported fields
}

Defines a TLS client certificate to present to certain hosts.

func (*TLSAuth) Certificate added in v0.17.2

func (c *TLSAuth) Certificate() (*tls.Certificate, error)

func (*TLSAuth) UnmarshalJSON added in v0.17.2

func (c *TLSAuth) UnmarshalJSON(data []byte) error

type TLSAuthFields added in v0.17.2

type TLSAuthFields struct {
	// Certificate and key as a PEM-encoded string, including "-----BEGIN CERTIFICATE-----".
	Cert string `json:"cert"`
	Key  string `json:"key"`

	// Domains to present the certificate to. May contain wildcards, eg. "*.example.com".
	Domains []string `json:"domains"`
}

Fields for TLSAuth. Unmarshalling hack.

type TLSCipherSuites added in v0.17.0

type TLSCipherSuites []uint16

A list of TLS cipher suites. Marshals and unmarshals from a list of names, eg. "TLS_ECDHE_RSA_WITH_RC4_128_SHA". BUG: This currently doesn't marshal back to JSON properly!!

func (*TLSCipherSuites) UnmarshalJSON added in v0.17.0

func (s *TLSCipherSuites) UnmarshalJSON(data []byte) error

type TLSVersion added in v0.17.0

type TLSVersion int

Describes a TLS version. Serialised to/from JSON as a string, eg. "tls1.2".

func (TLSVersion) MarshalJSON added in v0.19.0

func (v TLSVersion) MarshalJSON() ([]byte, error)

func (*TLSVersion) UnmarshalJSON added in v0.17.0

func (v *TLSVersion) UnmarshalJSON(data []byte) error

type TLSVersions added in v0.19.0

type TLSVersions TLSVersionsFields

Describes a set (min/max) of TLS versions.

func (*TLSVersions) UnmarshalJSON added in v0.19.0

func (v *TLSVersions) UnmarshalJSON(data []byte) error

type TLSVersionsFields added in v0.19.0

type TLSVersionsFields struct {
	Min TLSVersion `json:"min"` // Minimum allowed version, 0 = any.
	Max TLSVersion `json:"max"` // Maximum allowed version, 0 = any.
}

Fields for TLSVersions. Unmarshalling hack.

type VU

type VU interface {
	// Runs the VU once. The VU is responsible for handling the Halting Problem, eg. making sure
	// that execution actually stops when the context is cancelled.
	RunOnce(ctx context.Context) ([]stats.Sample, error)

	// Assign the VU a new ID. Called by the Executor upon creation, but may be called multiple
	// times if the VU is recycled because the test was scaled down and then back up.
	Reconfigure(id int64) error
}

A VU is a Virtual User, that can be scheduled by an Executor.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL