config

package
v1.3.15 Latest Latest
Warning

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

Go to latest
Published: May 19, 2023 License: MPL-2.0 Imports: 40 Imported by: 430

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// DefaultEnvDenylist is the default set of environment variables that are
	// filtered when passing the environment variables of the host to a task.
	DefaultEnvDenylist = strings.Join(host.DefaultEnvDenyList, ",")

	// DefaultUserDenylist is the default set of users that tasks are not
	// allowed to run as when using a driver in "user.checked_drivers"
	DefaultUserDenylist = strings.Join([]string{
		"root",
		"Administrator",
	}, ",")

	// DefaultUserCheckedDrivers is the set of drivers we apply the user
	// denylist onto. For virtualized drivers it often doesn't make sense to
	// make this stipulation so by default they are ignored.
	DefaultUserCheckedDrivers = strings.Join([]string{
		"exec",
		"qemu",
		"java",
	}, ",")

	// A mapping of directories on the host OS to attempt to embed inside each
	// task's chroot.
	DefaultChrootEnv = map[string]string{
		"/bin":            "/bin",
		"/etc":            "/etc",
		"/lib":            "/lib",
		"/lib32":          "/lib32",
		"/lib64":          "/lib64",
		"/run/resolvconf": "/run/resolvconf",
		"/sbin":           "/sbin",
		"/usr":            "/usr",

		"/run/systemd/resolve": "/run/systemd/resolve",
	}

	DefaultTemplateMaxStale = 87600 * time.Hour

	DefaultTemplateFunctionDenylist = []string{"plugin", "writeToFile"}
)

Functions

This section is empty.

Types

type AllocRunnerConfig added in v1.3.15

type AllocRunnerConfig struct {
	// Logger is the logger for the allocation runner.
	Logger log.Logger

	// ClientConfig is the clients configuration.
	ClientConfig *Config

	// Alloc captures the allocation that should be run.
	Alloc *structs.Allocation

	// StateDB is used to store and restore state.
	StateDB cstate.StateDB

	// Consul is the Consul client used to register task services and checks
	Consul serviceregistration.Handler

	// ConsulProxies is the Consul client used to lookup supported envoy versions
	// of the Consul agent.
	ConsulProxies consul.SupportedProxiesAPI

	// ConsulSI is the Consul client used to manage service identity tokens.
	ConsulSI consul.ServiceIdentityAPI

	// Vault is the Vault client to use to retrieve Vault tokens
	Vault vaultclient.VaultClient

	// StateUpdater is used to emit updated task state
	StateUpdater interfaces.AllocStateHandler

	// DeviceStatsReporter is used to lookup resource usage for alloc devices
	DeviceStatsReporter interfaces.DeviceStatsReporter

	// PrevAllocWatcher handles waiting on previous or preempted allocations
	PrevAllocWatcher PrevAllocWatcher

	// PrevAllocMigrator allows the migration of a previous allocations alloc dir
	PrevAllocMigrator PrevAllocMigrator

	// DynamicRegistry contains all locally registered dynamic plugins (e.g csi
	// plugins).
	DynamicRegistry dynamicplugins.Registry

	// CSIManager is used to wait for CSI Volumes to be attached, and by the task
	// runner to manage their mounting
	CSIManager csimanager.Manager

	// DeviceManager is used to mount devices as well as lookup device
	// statistics
	DeviceManager devicemanager.Manager

	// DriverManager handles dispensing of driver plugins
	DriverManager drivermanager.Manager

	// CpusetManager configures the cpuset cgroup if supported by the platform
	CpusetManager cgutil.CpusetManager

	// ServersContactedCh is closed when the first GetClientAllocs call to
	// servers succeeds and allocs are synced.
	ServersContactedCh chan struct{}

	// RPCClient is the RPC Client that should be used by the allocrunner and its
	// hooks to communicate with Nomad Servers.
	RPCClient RPCer

	// ServiceRegWrapper is the handler wrapper that is used by service hooks
	// to perform service and check registration and deregistration.
	ServiceRegWrapper *wrapper.HandlerWrapper

	// Getter is an interface for retrieving artifacts.
	Getter interfaces.ArtifactGetter
}

AllocRunnerConfig holds the configuration for creating an allocation runner.

type AllocRunnerFactory added in v1.3.15

type AllocRunnerFactory func(*AllocRunnerConfig) (arinterfaces.AllocRunner, error)

AllocRunnerFactory returns an AllocRunner interface built from the configuration. Note: the type for config is any because we can't count on test callers being able to make a real allocrunner.Config without an circular import

type ArtifactConfig added in v1.1.14

type ArtifactConfig struct {
	HTTPReadTimeout time.Duration
	HTTPMaxBytes    int64

	GCSTimeout time.Duration
	GitTimeout time.Duration
	HgTimeout  time.Duration
	S3Timeout  time.Duration

	DecompressionLimitSize      int64
	DecompressionLimitFileCount int
}

ArtifactConfig is the internal readonly copy of the client agent's ArtifactConfig.

func ArtifactConfigFromAgent added in v1.1.14

func ArtifactConfigFromAgent(c *config.ArtifactConfig) (*ArtifactConfig, error)

ArtifactConfigFromAgent creates a new internal readonly copy of the client agent's ArtifactConfig. The config should have already been validated.

func (*ArtifactConfig) Copy added in v1.1.14

func (a *ArtifactConfig) Copy() *ArtifactConfig

type ClientTemplateConfig added in v0.9.5

type ClientTemplateConfig struct {
	// FunctionDenylist disables functions in consul-template that
	// are unsafe because they expose information from the client host.
	FunctionDenylist []string `hcl:"function_denylist"`

	// Deprecated: COMPAT(1.0) consul-template uses inclusive language from
	// v0.25.0 - function_blacklist is kept for compatibility
	FunctionBlacklist []string `hcl:"function_blacklist"`

	// DisableSandbox allows templates to access arbitrary files on the
	// client host. By default templates can access files only within
	// the task directory.
	DisableSandbox bool `hcl:"disable_file_sandbox"`

	// This is the maximum interval to allow "stale" data. By default, only the
	// Consul leader will respond to queries; any requests to a follower will
	// forward to the leader. In large clusters with many requests, this is not as
	// scalable, so this option allows any follower to respond to a query, so long
	// as the last-replicated data is within these bounds. Higher values result in
	// less cluster load, but are more likely to have outdated data.
	// NOTE: Since Consul Template uses a pointer, this field uses a pointer which
	// is inconsistent with how Nomad typically works. This decision was made to
	// maintain parity with the external subsystem, not to establish a new standard.
	MaxStale    *time.Duration `hcl:"-"`
	MaxStaleHCL string         `hcl:"max_stale,optional"`

	// BlockQueryWaitTime is amount of time in seconds to do a blocking query for.
	// Many endpoints in Consul support a feature known as "blocking queries".
	// A blocking query is used to wait for a potential change using long polling.
	// NOTE: Since Consul Template uses a pointer, this field uses a pointer which
	// is inconsistent with how Nomad typically works. This decision was made to
	// maintain parity with the external subsystem, not to establish a new standard.
	BlockQueryWaitTime    *time.Duration `hcl:"-"`
	BlockQueryWaitTimeHCL string         `hcl:"block_query_wait,optional"`

	// Wait is the quiescence timers; it defines the minimum and maximum amount of
	// time to wait for the Consul cluster to reach a consistent state before rendering a
	// template. This is useful to enable in systems where Consul is experiencing
	// a lot of flapping because it will reduce the number of times a template is rendered.
	Wait *WaitConfig `hcl:"wait,optional" json:"-"`

	// WaitBounds allows operators to define boundaries on individual template wait
	// configuration overrides. If set, this ensures that if a job author specifies
	// a wait configuration with values the cluster operator does not allow, the
	// cluster operator's boundary will be applied rather than the job author's
	// out of bounds configuration.
	WaitBounds *WaitConfig `hcl:"wait_bounds,optional" json:"-"`

	// This controls the retry behavior when an error is returned from Consul.
	// Consul Template is highly fault tolerant, meaning it does not exit in the
	// face of failure. Instead, it uses exponential back-off and retry functions
	// to wait for the cluster to become available, as is customary in distributed
	// systems.
	ConsulRetry *RetryConfig `hcl:"consul_retry,optional"`

	// This controls the retry behavior when an error is returned from Vault.
	// Consul Template is highly fault tolerant, meaning it does not exit in the
	// face of failure. Instead, it uses exponential back-off and retry functions
	// to wait for the cluster to become available, as is customary in distributed
	// systems.
	VaultRetry *RetryConfig `hcl:"vault_retry,optional"`

	// This controls the retry behavior when an error is returned from Nomad.
	// Consul Template is highly fault tolerant, meaning it does not exit in the
	// face of failure. Instead, it uses exponential back-off and retry functions
	// to wait for the cluster to become available, as is customary in distributed
	// systems.
	NomadRetry *RetryConfig `hcl:"nomad_retry,optional"`
}

ClientTemplateConfig is configuration on the client specific to template rendering

func (*ClientTemplateConfig) Copy added in v0.10.0

Copy returns a deep copy of a ClientTemplateConfig

func (*ClientTemplateConfig) IsEmpty added in v1.2.4

func (c *ClientTemplateConfig) IsEmpty() bool

type Config

type Config struct {
	// DevMode controls if we are in a development mode which
	// avoids persistent storage.
	DevMode bool

	// EnableDebug is used to enable debugging RPC endpoints
	// in the absence of ACLs
	EnableDebug bool

	// StateDir is where we store our state
	StateDir string

	// AllocDir is where we store data for allocations
	AllocDir string

	// LogOutput is the destination for logs
	LogOutput io.Writer

	// Logger provides a logger to the client
	Logger log.InterceptLogger

	// Region is the clients region
	Region string

	// Network interface to be used in network fingerprinting
	NetworkInterface string

	// Network speed is the default speed of network interfaces if they can not
	// be determined dynamically.
	NetworkSpeed int

	// CpuCompute is the default total CPU compute if they can not be determined
	// dynamically. It should be given as Cores * MHz (2 Cores * 2 Ghz = 4000)
	CpuCompute int

	// MemoryMB is the default node total memory in megabytes if it cannot be
	// determined dynamically.
	MemoryMB int

	// MaxKillTimeout allows capping the user-specifiable KillTimeout. If the
	// task's KillTimeout is greater than the MaxKillTimeout, MaxKillTimeout is
	// used.
	MaxKillTimeout time.Duration

	// Servers is a list of known server addresses. These are as "host:port"
	Servers []string

	// RPCHandler can be provided to avoid network traffic if the
	// server is running locally.
	RPCHandler RPCHandler

	// Node provides the base node
	Node *structs.Node

	// ClientMaxPort is the upper range of the ports that the client uses for
	// communicating with plugin subsystems over loopback
	ClientMaxPort uint

	// ClientMinPort is the lower range of the ports that the client uses for
	// communicating with plugin subsystems over loopback
	ClientMinPort uint

	// MaxDynamicPort is the largest dynamic port generated
	MaxDynamicPort int

	// MinDynamicPort is the smallest dynamic port generated
	MinDynamicPort int

	// A mapping of directories on the host OS to attempt to embed inside each
	// task's chroot.
	ChrootEnv map[string]string

	// Options provides arbitrary key-value configuration for nomad internals,
	// like fingerprinters and drivers. The format is:
	//
	//	namespace.option = value
	Options map[string]string

	// Version is the version of the Nomad client
	Version *version.VersionInfo

	// ConsulConfig is this Agent's Consul configuration
	ConsulConfig *structsc.ConsulConfig

	// VaultConfig is this Agent's Vault configuration
	VaultConfig *structsc.VaultConfig

	// StatsCollectionInterval is the interval at which the Nomad client
	// collects resource usage stats
	StatsCollectionInterval time.Duration

	// PublishNodeMetrics determines whether nomad is going to publish node
	// level metrics to remote Telemetry sinks
	PublishNodeMetrics bool

	// PublishAllocationMetrics determines whether nomad is going to publish
	// allocation metrics to remote Telemetry sinks
	PublishAllocationMetrics bool

	// TLSConfig holds various TLS related configurations
	TLSConfig *structsc.TLSConfig

	// GCInterval is the time interval at which the client triggers garbage
	// collection
	GCInterval time.Duration

	// GCParallelDestroys is the number of parallel destroys the garbage
	// collector will allow.
	GCParallelDestroys int

	// GCDiskUsageThreshold is the disk usage threshold given as a percent
	// beyond which the Nomad client triggers GC of terminal allocations
	GCDiskUsageThreshold float64

	// GCInodeUsageThreshold is the inode usage threshold given as a percent
	// beyond which the Nomad client triggers GC of the terminal allocations
	GCInodeUsageThreshold float64

	// GCMaxAllocs is the maximum number of allocations a node can have
	// before garbage collection is triggered.
	GCMaxAllocs int

	// LogLevel is the level of the logs to putout
	LogLevel string

	// NoHostUUID disables using the host's UUID and will force generation of a
	// random UUID.
	NoHostUUID bool

	// ACLEnabled controls if ACL enforcement and management is enabled.
	ACLEnabled bool

	// ACLTokenTTL is how long we cache token values for
	ACLTokenTTL time.Duration

	// ACLPolicyTTL is how long we cache policy values for
	ACLPolicyTTL time.Duration

	// DisableRemoteExec disables remote exec targeting tasks on this client
	DisableRemoteExec bool

	// TemplateConfig includes configuration for template rendering
	TemplateConfig *ClientTemplateConfig

	// RPCHoldTimeout is how long an RPC can be "held" before it is errored.
	// This is used to paper over a loss of leadership by instead holding RPCs,
	// so that the caller experiences a slow response rather than an error.
	// This period is meant to be long enough for a leader election to take
	// place, and a small jitter is applied to avoid a thundering herd.
	RPCHoldTimeout time.Duration

	// PluginLoader is used to load plugins.
	PluginLoader loader.PluginCatalog

	// PluginSingletonLoader is a plugin loader that will returns singleton
	// instances of the plugins.
	PluginSingletonLoader loader.PluginCatalog

	// StateDBFactory is used to override stateDB implementations,
	StateDBFactory state.NewStateDBFunc

	AllocRunnerFactory AllocRunnerFactory

	// CNIPath is the path used to search for CNI plugins. Multiple paths can
	// be specified with colon delimited
	CNIPath string

	// CNIConfigDir is the directory where CNI network configuration is located. The
	// client will use this path when fingerprinting CNI networks.
	CNIConfigDir string

	// CNIInterfacePrefix is the prefix to use when creating CNI network interfaces. This
	// defaults to 'eth', therefore the first interface created by CNI inside the alloc
	// network will be 'eth0'.
	CNIInterfacePrefix string

	// BridgeNetworkName is the name to use for the bridge created in bridge
	// networking mode. This defaults to 'nomad' if not set
	BridgeNetworkName string

	// BridgeNetworkAllocSubnet is the IP subnet to use for address allocation
	// for allocations in bridge networking mode. Subnet must be in CIDR
	// notation
	BridgeNetworkAllocSubnet string

	// HostVolumes is a map of the configured host volumes by name.
	HostVolumes map[string]*structs.ClientHostVolumeConfig

	// HostNetworks is a map of the conigured host networks by name.
	HostNetworks map[string]*structs.ClientHostNetworkConfig

	// BindWildcardDefaultHostNetwork toggles if the default host network should accept all
	// destinations (true) or only filter on the IP of the default host network (false) when
	// port mapping. This allows Nomad clients with no defined host networks to accept and
	// port forward traffic only matching on the destination port. An example use of this
	// is when a network loadbalancer is utilizing direct server return and the destination
	// address of incomming packets does not match the IP address of the host interface.
	//
	// This configuration is only considered if no host networks are defined.
	BindWildcardDefaultHostNetwork bool

	// CgroupParent is the parent cgroup Nomad should use when managing any cgroup subsystems.
	// Currently this only includes the 'cpuset' cgroup subsystem.
	CgroupParent string

	// ReservableCores if set overrides the set of reservable cores reported in fingerprinting.
	ReservableCores []uint16

	// NomadServiceDiscovery determines whether the Nomad native service
	// discovery client functionality is enabled.
	NomadServiceDiscovery bool

	// TemplateDialer is our custom HTTP dialer for consul-template. This is
	// used for template functions which require access to the Nomad API.
	TemplateDialer *bufconndialer.BufConnWrapper

	// Artifact configuration from the agent's config file.
	Artifact *ArtifactConfig

	// ExtraAllocHooks are run with other allocation hooks, mainly for testing.
	ExtraAllocHooks []interfaces.RunnerHook
}

Config is used to parameterize and configure the behavior of the client

func DefaultConfig added in v0.4.0

func DefaultConfig() *Config

DefaultConfig returns the default configuration

func TestClientConfig added in v0.9.0

func TestClientConfig(t testing.T) (*Config, func())

TestClientConfig returns a default client configuration for test clients and a cleanup func to remove the state and alloc dirs when finished.

func (*Config) Copy added in v0.3.0

func (c *Config) Copy() *Config

func (*Config) NomadPluginConfig added in v0.9.0

func (c *Config) NomadPluginConfig() *base.AgentConfig

NomadPluginConfig produces the NomadConfig struct which is sent to Nomad plugins

func (*Config) Read

func (c *Config) Read(id string) string

Read returns the specified configuration value or "".

func (*Config) ReadAlternativeDefault added in v1.0.0

func (c *Config) ReadAlternativeDefault(ids []string, defaultValue string) string

ReadAlternativeDefault returns the specified configuration value, or the specified value if none is set.

func (*Config) ReadBool added in v0.2.0

func (c *Config) ReadBool(id string) (bool, error)

ReadBool parses the specified option as a boolean.

func (*Config) ReadBoolDefault added in v0.2.0

func (c *Config) ReadBoolDefault(id string, defaultValue bool) bool

ReadBoolDefault tries to parse the specified option as a boolean. If there is an error in parsing, the default option is returned.

func (*Config) ReadDefault

func (c *Config) ReadDefault(id string, defaultValue string) string

ReadDefault returns the specified configuration value, or the specified default value if none is set.

func (*Config) ReadDuration added in v0.5.5

func (c *Config) ReadDuration(id string) (time.Duration, error)

ReadDuration parses the specified option as a duration.

func (*Config) ReadDurationDefault added in v0.5.5

func (c *Config) ReadDurationDefault(id string, defaultValue time.Duration) time.Duration

ReadDurationDefault tries to parse the specified option as a duration. If there is an error in parsing, the default option is returned.

func (*Config) ReadInt added in v0.5.5

func (c *Config) ReadInt(id string) (int, error)

ReadInt parses the specified option as a int.

func (*Config) ReadIntDefault added in v0.5.5

func (c *Config) ReadIntDefault(id string, defaultValue int) int

ReadIntDefault tries to parse the specified option as a int. If there is an error in parsing, the default option is returned.

func (*Config) ReadStringListAlternativeToMapDefault added in v1.0.0

func (c *Config) ReadStringListAlternativeToMapDefault(keys []string, defaultValue string) map[string]struct{}

ReadStringListAlternativeToMapDefault tries to parse the specified options as a comma sparated list. If there is an error in parsing, an empty list is returned.

func (*Config) ReadStringListToMap added in v0.2.1

func (c *Config) ReadStringListToMap(keys ...string) map[string]struct{}

ReadStringListToMap tries to parse the specified option(s) as a comma separated list. If there is an error in parsing, an empty list is returned.

func (*Config) ReadStringListToMapDefault added in v0.3.2

func (c *Config) ReadStringListToMapDefault(key, defaultValue string) map[string]struct{}

ReadStringListToMapDefault tries to parse the specified option as a comma separated list. If there is an error in parsing, an empty list is returned.

type PrevAllocMigrator added in v1.3.15

type PrevAllocMigrator interface {
	PrevAllocWatcher

	// IsMigrating returns true if a concurrent caller is in Migrate
	IsMigrating() bool

	// Migrate data from previous alloc
	Migrate(ctx context.Context, dest *allocdir.AllocDir) error
}

PrevAllocMigrator allows AllocRunners to migrate a previous allocation whether or not the previous allocation is local or remote.

type PrevAllocWatcher added in v1.3.15

type PrevAllocWatcher interface {
	// Wait for previous alloc to terminate
	Wait(context.Context) error

	// IsWaiting returns true if a concurrent caller is blocked in Wait
	IsWaiting() bool
}

PrevAllocWatcher allows AllocRunners to wait for a previous allocation to terminate whether or not the previous allocation is local or remote. See `PrevAllocMigrator` for migrating workloads.

type RPCHandler

type RPCHandler interface {
	RPC(method string, args interface{}, reply interface{}) error
}

RPCHandler can be provided to the Client if there is a local server to avoid going over the network. If not provided, the Client will maintain a connection pool to the servers

type RPCer added in v1.3.15

type RPCer interface {
	RPC(method string, args interface{}, reply interface{}) error
}

RPCer is the interface needed by hooks to make RPC calls.

type RetryConfig added in v1.2.4

type RetryConfig struct {
	// Attempts is the total number of maximum attempts to retry before letting
	// the error fall through.
	// 0 means unlimited.
	Attempts *int `hcl:"attempts,optional"`
	// Backoff is the base of the exponential backoff. This number will be
	// multiplied by the next power of 2 on each iteration.
	Backoff    *time.Duration `hcl:"-"`
	BackoffHCL string         `hcl:"backoff,optional" json:"-"`
	// MaxBackoff is an upper limit to the sleep time between retries
	// A MaxBackoff of 0 means there is no limit to the exponential growth of the backoff.
	MaxBackoff    *time.Duration `hcl:"-"`
	MaxBackoffHCL string         `hcl:"max_backoff,optional" json:"-"`
}

RetryConfig is mirrored from templateconfig.WaitConfig because we need to handle the HCL indirection to support mapping in agent.ParseConfigFile. NOTE: Since Consul Template requires pointers, this type uses pointers to fields which is inconsistent with how Nomad typically works. However, since zero in Attempts and MaxBackoff have special meaning, it is necessary to know if the value was actually set rather than if it defaulted to 0. The rest of the fields use pointers to maintain parity with the external subystem, not to establish a new standard.

func (*RetryConfig) Copy added in v1.2.4

func (rc *RetryConfig) Copy() *RetryConfig

func (*RetryConfig) Equals added in v1.2.4

func (rc *RetryConfig) Equals(other *RetryConfig) bool

Equals returns the result of reflect.DeepEqual

func (*RetryConfig) IsEmpty added in v1.2.4

func (rc *RetryConfig) IsEmpty() bool

IsEmpty returns true if the receiver only contains an instance with no fields set.

func (*RetryConfig) Merge added in v1.2.4

func (rc *RetryConfig) Merge(b *RetryConfig) *RetryConfig

Merge merges two RetryConfigs. The passed instance always takes precedence.

func (*RetryConfig) ToConsulTemplate added in v1.2.4

func (rc *RetryConfig) ToConsulTemplate() (*config.RetryConfig, error)

ToConsulTemplate converts a client RetryConfig instance to a consul-template RetryConfig

func (*RetryConfig) Validate added in v1.2.4

func (rc *RetryConfig) Validate() error

Validate returns an error if the receiver is nil or empty, or if Backoff is greater than MaxBackoff.

type WaitConfig added in v1.2.4

type WaitConfig struct {
	Min    *time.Duration `hcl:"-"`
	MinHCL string         `hcl:"min,optional" json:"-"`
	Max    *time.Duration `hcl:"-"`
	MaxHCL string         `hcl:"max,optional" json:"-"`
}

WaitConfig is mirrored from templateconfig.WaitConfig because we need to handle the HCL conversion which happens in agent.ParseConfigFile NOTE: Since Consul Template requires pointers, this type uses pointers to fields which is inconsistent with how Nomad typically works. This decision was made to maintain parity with the external subsystem, not to establish a new standard.

func (*WaitConfig) Copy added in v1.2.4

func (wc *WaitConfig) Copy() *WaitConfig

Copy returns a deep copy of the receiver.

func (*WaitConfig) Equals added in v1.2.4

func (wc *WaitConfig) Equals(other *WaitConfig) bool

Equals returns the result of reflect.DeepEqual

func (*WaitConfig) IsEmpty added in v1.2.4

func (wc *WaitConfig) IsEmpty() bool

IsEmpty returns true if the receiver only contains an instance with no fields set.

func (*WaitConfig) Merge added in v1.2.4

func (wc *WaitConfig) Merge(b *WaitConfig) *WaitConfig

Merge merges two WaitConfigs. The passed instance always takes precedence.

func (*WaitConfig) ToConsulTemplate added in v1.2.4

func (wc *WaitConfig) ToConsulTemplate() (*config.WaitConfig, error)

ToConsulTemplate converts a client WaitConfig instance to a consul-template WaitConfig

func (*WaitConfig) Validate added in v1.2.4

func (wc *WaitConfig) Validate() error

Validate returns an error if the receiver is nil or empty or if Min is greater than Max the user specified Max.

Jump to

Keyboard shortcuts

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