mdlib

package module
v0.0.0-...-e6a681a Latest Latest
Warning

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

Go to latest
Published: Jun 28, 2023 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// ClusterResourceType is the keyword used to classify the resource type for clusters.
	ClusterResourceType = "cluster"

	// LoadBalancerResourceType is the keyword used to classify the resource type for classic elastic load balancers.
	LoadBalancerResourceType = "classic-load-balancer"
	// ApplicationLoadBalancerResourceType is the keyword used to classify the resource type for an application load balancer.
	ApplicationLoadBalancerResourceType = "application-load-balancer"
	// NetworkLoadBalancerResourceType is the keyword to classify the resource type for an network load balancer
	NetworkLoadBalancerResourceType = "network-load-balancer"

	// SecurityGroupResourceType is the keyword used to classify the resource type for security groups.
	SecurityGroupResourceType = "security-group"

	// AWSCloudProvider is the keyword used to classify that a resource is intended for AWS
	AWSCloudProvider = "aws"
	// TitusCloudProvider is the keyword used to classify that a resource is intended for Titus
	TitusCloudProvider = "titus"

	// DebianArtifactType is the keyword to to classify a debian artifact
	DebianArtifactType = "deb"
	// DockerArtifactType is the keyword to to classify a docker image artifact
	DockerArtifactType = "docker"
)

Variables

View Source
var (
	// DefaultDeliveryConfigFileName is the default name of the delivery config file for spinnaker managed delivery.
	DefaultDeliveryConfigFileName = "spinnaker.yml"

	// DefaultDeliveryConfigDirName is the default directory where the delivery config file is read and written to.
	DefaultDeliveryConfigDirName = "."

	// DefaultEnvironmentConstraint is the default constraint for an added environment while exporting new resources.
	DefaultEnvironmentConstraint interface{} = map[string]string{
		"type": "manual-judgement",
	}
)
View Source
var ConfigKeySortPriority = []string{
	"kind",
	"name",
	"type",
	"moniker",
	"artifactReference",
	"container",
	"locations",
	"application",
	"artifacts",
	"environments",
}

ConfigKeySortPriority is used to sort the maps in the delivery config yaml document so "important" fields appear near the top

View Source
var (
	// DefaultSpinnakerAPIBaseURL is the base url to be used for app spinnaker api calls. It can be set in code
	// or overridden with the SPINNAKER_API_BASE_URL environment variable.
	DefaultSpinnakerAPIBaseURL = os.Getenv("SPINNAKER_API_BASE_URL")
)

Functions

func ExportArtifact

func ExportArtifact(cli *Client, resource *ExportableResource, result interface{}) error

ExportArtifact will contact the Spinnaker REST API to collect the YAML delivery config representation for the artifacts for the given cluster

func ExportResource

func ExportResource(cli *Client, resource *ExportableResource) ([]byte, error)

ExportResource will contact the Spinnaker REST API to collect the YAML delivery config representation for a specific resource.

func GetCredential

func GetCredential(cli *Client, account string, result interface{}) error

GetCredential populates the credential result structure for the spinnaker account provided. Unless a custom result type is required, *Credential is recommended

func GetLoadBalancers

func GetLoadBalancers(cli *Client, appName string, result interface{}) error

GetLoadBalancers populates the load balancers result structure for spinnaker application appName. Unless a custom result type is required, *[]LoadBalancer is recommended.

func GetSecurityGroups

func GetSecurityGroups(cli *Client, appName string, result interface{}) error

GetSecurityGroups populates the security groups result structure for spinnaker account provided. Unless a custom result type is required, *[]SecurityGroup is recommended.

func GetServerGroups

func GetServerGroups(cli *Client, appName string, result interface{}) error

GetServerGroups populates the server groups result structure for spinnaker application appName. Unless a custom result type is required, *[]ServerGroup is recommended.

func PauseManagement

func PauseManagement(cli *Client, appName string) error

PauseManagement will cause Spinnaker to pause managing the state of the application. Management history will be reserved and can be resumed later.

func ResumeManagement

func ResumeManagement(cli *Client, appName string) error

ResumeManagement will cause Spinnaker to resume managing the state of the application, assuming it had been previously paused.

Types

type Account

type Account = string

Account is just a string type, used to make code more readable

type ActuationPlan

type ActuationPlan struct {
	Application      string            `json:"application" yaml:"application"`
	UpdatedAt        time.Time         `json:"updatedAt" yaml:"updatedAt"`
	EnvironmentPlans []EnvironmentPlan `json:"environmentPlans" yaml:"environmentPlans"`
	Errors           []string          `json:"errors" yaml:"errors"`
}

ActuationPlan describes the action MD will take to reconcile the state of the world with the desired state

type ApplicationResources

type ApplicationResources struct {
	AppName        string
	ServerGroups   []ServerGroup
	LoadBalancers  []LoadBalancer
	SecurityGroups []SecurityGroup
}

ApplicationResources is used to track all the resources for an application as populated from FinndApplicationResources.

func FindApplicationResources

func FindApplicationResources(cli *Client, appName string) (*ApplicationResources, error)

FindApplicationResources will collect application resources from various Spinnaker REST APIs, loading resources in parallel when possible.

type ArtifactSorter

type ArtifactSorter []*DeliveryArtifact

ArtifactSorter is a wrapper to help sort DeliveryArtifacts

func (ArtifactSorter) Len

func (s ArtifactSorter) Len() int

Len fulfills the sort.Interface requirement

func (ArtifactSorter) Less

func (s ArtifactSorter) Less(i, j int) bool

Less fulfills the sort.Interface requirement

func (ArtifactSorter) Swap

func (s ArtifactSorter) Swap(i, j int)

Swap fulfills the sort.Interface requirement

type BuildInfo

type BuildInfo struct {
	PackageName string                      `json:"package_name"`
	Jenkins     ServerGroupJenkinsBuildInfo `json:"jenkins"`
	Docker      ServerGroupDockerBuildInfo  `json:"docker"`
}

BuildInfo contains information about the build artifact that is deployed to the server group

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client holds details for connecting to the spinnaker REST API.

func NewClient

func NewClient(opts ...ClientOpt) *Client

NewClient constructs a Client and applies any provided ClientOpt

type ClientOpt

type ClientOpt func(*Client)

ClientOpt is an interface for variadic options when constructing a Client via NewClient

func WithBaseURL

func WithBaseURL(baseURL string) ClientOpt

WithBaseURL is a ClientOpt to set the spinnakerAPIBaseURL via NewClient

func WithHTTPClient

func WithHTTPClient(client func(*http.Request) (*http.Response, error)) ClientOpt

WithHTTPClient is a ClientOpt to set the httpClient via NewClient

type Credential

type Credential struct {
	PrimaryAccount bool   `json:"primaryAccount"`
	CloudProvider  string `json:"cloudProvider"`
	AWSAccount     string `json:"awsAccount"`
}

Credential contains account status

type DeliveryArtifact

type DeliveryArtifact struct {
	Name               string
	Type               string
	Reference          string `json:"reference,omitempty" yaml:"reference,omitempty"`
	TagVersionStrategy string `json:"tagVersionStrategy.omitempty" yaml:"tagVersionStrategy,omitempty"`
	VMOptions          struct {
		BaseLabel string   `json:"baseLabel,omitempty" yaml:"baseLabel,omitempty"`
		BaseOS    string   `json:"baseOs,omitempty" yaml:"baseOs,omitempty"`
		Regions   []string `json:"regions,omitempty" yaml:"regions,omitempty"`
		StoreType string   `json:"storeType,omitempty" yaml:"storeType,omitempty"`
	} `json:"vmOptions,omitempty" yaml:"vmOptions,omitempty"`
	From struct {
		Branch struct {
			Name       string `json:"name,omitempty" yaml:"name,omitempty"`
			StartsWith string `json:"startsWith,omitempty" yaml:"startsWith,omitempty"`
			Regex      string `json:"regex,omitempty" yaml:"regex,omitempty"`
		} `json:"branch,omitempty" yaml:"branch,omitempty"`
		PullRequestOnly bool `json:"pullRequestOnly,omitempty" yaml:"pullRequestOnly,omitempty"`
	} `json:"from,omitempty" yaml:"from,omitempty"`
}

DeliveryArtifact holds artifact details used for managed delivery

func (*DeliveryArtifact) Equal

func (a *DeliveryArtifact) Equal(b *DeliveryArtifact) bool

Equal compares the artifact TagVersionStrategy and VMOptions

func (*DeliveryArtifact) RefName

func (a *DeliveryArtifact) RefName() string

RefName returns the Reference value for comparisons. it will use the Reference value if defined, otherwise default to the Name value.

type DeliveryConfig

type DeliveryConfig struct {
	Name         string
	Application  string
	Artifacts    []*DeliveryArtifact
	Environments []*DeliveryEnvironment
}

DeliveryConfig holds the structure for the manage delivery config stored in .netflix/spinnaker.yml

type DeliveryConfigProcessor

type DeliveryConfigProcessor struct {
	// contains filtered or unexported fields
}

DeliveryConfigProcessor is a structure to manage operations on a delivery config.

func NewDeliveryConfigProcessor

func NewDeliveryConfigProcessor(opts ...ProcessorOption) *DeliveryConfigProcessor

NewDeliveryConfigProcessor will create a DeliveryConfigProcessor and apply all provided options.

func (*DeliveryConfigProcessor) AllEnvironments

func (p *DeliveryConfigProcessor) AllEnvironments() []string

AllEnvironments will return a list of the names of all the environments in the delivery config as well as the default/recommended environment names: testing, staging, and production.

func (*DeliveryConfigProcessor) Delete

func (p *DeliveryConfigProcessor) Delete(cli *Client) error

Delete will stop the delivery config from being managed, and will cause Spinnaker to remove all historical state about managing this configuration.

func (*DeliveryConfigProcessor) Diff

Diff will compare the delivery config file on disk with the currently deployed state of the Spinnaker application and report any changes. This can also be used to validate a delivery config (errors will be returned when an invalid delivery config is submitted).

func (*DeliveryConfigProcessor) InsertArtifact

func (p *DeliveryConfigProcessor) InsertArtifact(artifact *DeliveryArtifact) (added bool, updatedRef string)

InsertArtifact will add an artifact to the delivery config if it is not already present.

func (*DeliveryConfigProcessor) Load

func (p *DeliveryConfigProcessor) Load() error

Load will load the delivery config files from disk.

func (*DeliveryConfigProcessor) Plan

Plan sends the delivery config to Spinnaker to get the actuation plan

func (*DeliveryConfigProcessor) Publish

func (p *DeliveryConfigProcessor) Publish(cli *Client, force bool) error

Publish will post the delivery config to the Spinnaker API so that Spinnaker will update the Managed state for the application.

func (*DeliveryConfigProcessor) ResourceExists

func (p *DeliveryConfigProcessor) ResourceExists(search *ExportableResource) bool

ResourceExists returns true if the provided resource is found currently in the delivery config.

func (*DeliveryConfigProcessor) Save

func (p *DeliveryConfigProcessor) Save() error

Save will serialize the delivery config to disk.

func (*DeliveryConfigProcessor) UpdateArtifactReference

func (p *DeliveryConfigProcessor) UpdateArtifactReference(content *[]byte, updatedRef string) error

UpdateArtifactReference will update the artifact reference in the delivery config

func (*DeliveryConfigProcessor) UpsertResource

func (p *DeliveryConfigProcessor) UpsertResource(resource *ExportableResource, envName string, content []byte) (added bool, err error)

UpsertResource will update (if exists) or insert (if new) a resource into the delivery config. The resource will be added to the environment that corresponds to envName if the resource is new.

func (*DeliveryConfigProcessor) Validate

Validate posts the delivery config to the validation api and returns nil on success, or a ValidationErrorDetail

func (*DeliveryConfigProcessor) WhichEnvironment

func (p *DeliveryConfigProcessor) WhichEnvironment(resource *ExportableResource) string

WhichEnvironment will return the environment name for the given resource found in the delivery config. It will return an empty string if the resource is not found in any environment.

type DeliveryEnvironment

type DeliveryEnvironment struct {
	Name      string
	Locations DeliveryResourceLocations
	Resources []*DeliveryResource
}

DeliveryEnvironment contains the resources per environment.

type DeliveryImageProvider

type DeliveryImageProvider struct {
	DeliveryArtifact DeliveryArtifact `json:"deliveryArtifact" yaml:"deliveryArtifact"`
}

DeliveryImageProvider contains the artifact details used to make the image

type DeliveryResource

type DeliveryResource struct {
	Kind string
	Spec DeliveryResourceSpec
}

DeliveryResource contains the necessary configuration for a managed delivery resource

func (DeliveryResource) Account

func (r DeliveryResource) Account() string

Account return the account for the delivery resource

func (DeliveryResource) CloudProvider

func (r DeliveryResource) CloudProvider() string

CloudProvider returns the cloud provider for a resource. Currently it only return titus or aws

func (*DeliveryResource) Match

Match will return true if the ExportableResource matches the Kind, CloudProvider, Account and Name properties

func (DeliveryResource) Name

func (r DeliveryResource) Name() string

Name returns the name for the type of delivery resource

func (*DeliveryResource) ResourceType

func (r *DeliveryResource) ResourceType() string

ResourceType will return the inferred type from the Kind property, `ec2/cluster@v1` will return `cluster`

type DeliveryResourceContainer

type DeliveryResourceContainer struct {
	Image              string `json:"image" yaml:"image"`
	Organization       string `json:"organization" yaml:"organization"`
	TagVersionStrategy string `json:"tagVersionStrategy" yaml:"tagVersionStrategy"`
}

DeliveryResourceContainer contains details about the image deployed for a container.

type DeliveryResourceLocationRegion

type DeliveryResourceLocationRegion struct {
	Name string
}

DeliveryResourceLocationRegion contains the region name

type DeliveryResourceLocations

type DeliveryResourceLocations struct {
	Account string
	Regions []DeliveryResourceLocationRegion
}

DeliveryResourceLocations contains location details for delivery resources

func (DeliveryResourceLocations) Empty

func (l DeliveryResourceLocations) Empty() bool

Empty will return true if the DeliveryResourceLocations has no values set

type DeliveryResourceSpec

type DeliveryResourceSpec struct {
	Moniker       Moniker
	Locations     DeliveryResourceLocations
	ImageProvider DeliveryImageProvider     `json:"imageProvider" yaml:"imageProvider"`
	ArtifactName  string                    `json:"artifactName" yaml:"artifactName"`
	Container     DeliveryResourceContainer `json:"container" yaml:"container"`
}

DeliveryResourceSpec is the spec for the delivery resource

type EnvironmentPlan

type EnvironmentPlan struct {
	Environment   string         `json:"environment" yaml:"environment"`
	ResourcePlans []ResourcePlan `json:"resourcePlans" yaml:"resourcePlans"`
}

EnvironmentPlan describes the actions for a given environment

type ErrorInvalidContent

type ErrorInvalidContent struct {
	Content    []byte
	ParseError error
}

ErrorInvalidContent will occur when content is unable to be parsed.

func (ErrorInvalidContent) Error

func (e ErrorInvalidContent) Error() string

Error returns the parse error message.

type ErrorUnexpectedResponse

type ErrorUnexpectedResponse struct {
	StatusCode int
	URL        string
	Content    []byte
}

ErrorUnexpectedResponse will capture request details upon error.

func (ErrorUnexpectedResponse) Error

func (e ErrorUnexpectedResponse) Error() string

Error returns the error message

func (ErrorUnexpectedResponse) Parse

func (e ErrorUnexpectedResponse) Parse(data interface{}) error

Parse will attempt to populate the data from the content of the failed request.

type ExportableResource

type ExportableResource struct {
	ResourceType  string
	CloudProvider string
	Account       string
	Name          string
}

ExportableResource is structure to contain the necessary information to uniquely identify a resource stored in the delivery config or to export from Spinnaker API.

func ExportableApplicationResources

func ExportableApplicationResources(appData *ApplicationResources) []*ExportableResource

ExportableApplicationResources will return a list of ExportableResources that are found from the currently deployed application resources.

func (ExportableResource) HasKind

func (r ExportableResource) HasKind(kind string) bool

HasKind will return true if the resource matches the provided kind.

func (ExportableResource) String

func (r ExportableResource) String() string

String returns a useful formatting string to display an ExportableResource.

type Instance

type Instance struct {
	ID               string           `json:"id"`
	Name             string           `json:"name"`
	Health           []InstanceHealth `json:"health"`
	HealthState      string           `json:"healthState"`
	LaunchTime       int64            `json:"launchTime"`
	AvailabilityZone string           `json:"availabilityZone"`
}

Instance is a spinnaker instance of a deployable artifact. This can represent an AWS EC2 instance or a titus container.

type InstanceHealth

type InstanceHealth struct {
	Type  string `json:"type"`
	State string `json:"state"`
}

InstanceHealth is the health of a spinnaker instance for its deployment targets.

type LoadBalancer

type LoadBalancer struct {
	Name             string                    `json:"name"`
	Account          string                    `json:"account"`
	Region           string                    `json:"region"`
	Type             string                    `json:"type"`
	LoadBalancerType string                    `json:"loadBalancerType"`
	SecurityGroups   []string                  `json:"securityGroups"`
	ServerGroups     []LoadBalancerServerGroup `json:"serverGroups"`
	TargetGroups     []LoadBalancerTargetGroup `json:"targetGroups"`
}

LoadBalancer contains details about a load balancer, TargetGroups will be populated if it is an ALB. ServerGroups will be populated if it is an ELB.

type LoadBalancerServerGroup

type LoadBalancerServerGroup struct {
	Name string `json:"name"`
}

LoadBalancerServerGroup contains the server groups for an ELB

type LoadBalancerTargetGroup

type LoadBalancerTargetGroup struct {
	Name string `json:"name"`
}

LoadBalancerTargetGroup contains the name of the target group for an ALB

type Logger

type Logger interface {
	Printf(format string, v ...any)
	Noticef(format string, v ...any)
	Errorf(format string, v ...any)
}

Logger is a simple interface to abstract the logger implementation. Go core `log` is used by default.

func NewDefaultLogger

func NewDefaultLogger() Logger

NewDefaultLogger returns the default logger

type ManagedResourceDiff

type ManagedResourceDiff struct {
	Status     string                  `json:"status" yaml:"status"`
	ResourceID string                  `json:"resourceId" yaml:"resourceId"`
	Resource   DeliveryResource        `json:"resource" yaml:"resource"`
	Diffs      map[string]ResourceDiff `json:"diff" yaml:"diff"`
}

ManagedResourceDiff contains the details about a specific resource and if it has diffs

type Moniker

type Moniker struct {
	App      string `json:"app"`
	Cluster  string `json:"cluster"`
	Detail   string `json:"detail"`
	Stack    string `json:"stack"`
	Sequence int    `json:"sequence"`
}

Moniker is a spinnaker naming strategy. Every resource is assigned a Moniker which contains identification metadata.

func (Moniker) String

func (m Moniker) String() string

type ProcessorOption

type ProcessorOption func(p *DeliveryConfigProcessor)

ProcessorOption is the interface to provide variadic options to NewDeliveryConfigProcessor

func WithAppName

func WithAppName(a string) ProcessorOption

WithAppName is a ProcessorOption to set the name of the Spinnaker application name that the delivery config corresponds to. It is only necessary to set when exporting/creating a delivery config.

func WithConstraintsProvider

func WithConstraintsProvider(cp func(envName string, current DeliveryConfig) []interface{}) ProcessorOption

WithConstraintsProvider is a ProcessorOption to allow customizing how a default environment constraint is generated for newly created environments.

func WithDirectory

func WithDirectory(d string) ProcessorOption

WithDirectory is a ProcessorOption to set the directory where the delivery config is stored.

func WithFile

func WithFile(f string) ProcessorOption

WithFile is a ProcessorOption to set the name of the delivery config file.

func WithLogger

func WithLogger(l Logger) ProcessorOption

WithLogger is a ProcessorOption to specify which logger to use

func WithNotificationsProvider

func WithNotificationsProvider(np func(envName string, current DeliveryConfig) []interface{}) ProcessorOption

WithNotificationsProvider is a ProcessorOption to allow customizing how a default environment notification is generated for newly created environments.

func WithPostDeployProvider

func WithPostDeployProvider(vp func(envName string, current DeliveryConfig) []interface{}) ProcessorOption

WithPostDeployProvider is a ProcessorOption to allow customizing how a post deploy action is generated

func WithVerifyProvider

func WithVerifyProvider(vp func(envName string, current DeliveryConfig) []interface{}) ProcessorOption

WithVerifyProvider is a ProcessorOption to allow customizing how a environment verification is generated

func WithYAMLMarshal

func WithYAMLMarshal(marshaller func(interface{}) ([]byte, error)) ProcessorOption

WithYAMLMarshal is a ProcessorOption to allow customizing how the delivery config is serialized to disk.

func WithYAMLUnmarshal

func WithYAMLUnmarshal(unmarshaller func([]byte, interface{}) error) ProcessorOption

WithYAMLUnmarshal is a ProcessorOption to allow customizing how the delivery config is loaded from disk.

type ResourceAction

type ResourceAction string

ResourceAction describes the type of operation - NONE, CREATE or UPDATE

type ResourceDiff

type ResourceDiff struct {
	State   string `json:"state" yaml:"state"`
	Desired string `json:"desired" yaml:"desired"`
	Current string `json:"current" yaml:"current"`
}

ResourceDiff contains the exact records that differ

type ResourcePlan

type ResourcePlan struct {
	Environment         string               `json:"environment" yaml:"environment"`
	ResourceId          string               `json:"resourceId" yaml:"resourceId"`
	ResourceDisplayName string               `json:"resourceDisplayName" yaml:"resourceDisplayName"`
	IsManaged           bool                 `json:"isManaged" yaml:"isManaged"`
	IsPaused            bool                 `json:"isPaused" yaml:"isPaused"`
	Action              ResourceAction       `json:"action" yaml:"action"`
	Diff                []SingleResourceDiff `json:"diff" yaml:"diff"`
}

ResourcePlan describes the actions for a given resource

type ResourceSorter

type ResourceSorter []*ExportableResource

ResourceSorter is a wrapper to help sort ExportableResources

func (ResourceSorter) Len

func (s ResourceSorter) Len() int

Len fulfills the sort.Interface requirement

func (ResourceSorter) Less

func (s ResourceSorter) Less(i, j int) bool

Less fulfills the sort.Interface requirement

func (ResourceSorter) Swap

func (s ResourceSorter) Swap(i, j int)

Swap fulfills the sort.Interface requirement

type SecurityGroup

type SecurityGroup struct {
	Name    string `json:"name"`
	ID      string `json:"id"`
	Region  string `json:"region"`
	Account string `json:"account"`
}

SecurityGroup contains the relevant detail for mapping a SG id to a SG name.

type ServerGroup

type ServerGroup struct {
	Name           string     `json:"name"`
	Application    string     `json:"application"`
	Region         string     `json:"region"`
	Account        string     `json:"account"`
	Type           string     `json:"type"`
	Moniker        Moniker    `json:"moniker"`
	Instances      []Instance `json:"instances"`
	LoadBalancers  []string   `json:"loadBalancers"`
	TargetGroups   []string   `json:"targetGroups"`
	SecurityGroups []string   `json:"securityGroups"`
	BuildInfo      BuildInfo  `json:"buildInfo"`
}

ServerGroup is a collection of instances of the running software deployed from spinnaker. It identifies the deployable artifact and contain basic configuration settings suchas number of instances, autoscaling policies metadata, etc.

type ServerGroupDockerBuildInfo

type ServerGroupDockerBuildInfo struct {
	Image  string `json:"image"`
	Tag    string `json:"tag"`
	Digest string `json:"digest"`
}

ServerGroupDockerBuildInfo contains docker specific information from the server group buildInfo

type ServerGroupJenkinsBuildInfo

type ServerGroupJenkinsBuildInfo struct {
	Name   string `json:"name"`
	Number string `json:"number"`
	Host   string `json:"host"`
}

ServerGroupJenkinsBuildInfo contains jenkins specific information from the server group buildInfo

type SingleResourceDiff

type SingleResourceDiff struct {
	Field   string `json:"field" yaml:"field"`
	Type    string `json:"type" yaml:"type"`
	Desired string `json:"desired" yaml:"desired"`
	Current string `json:"current" yaml:"current"`
	Message string `json:"message" yaml:"message"`
	Prefix  string `json:"prefix" yaml:"prefix"`
}

SingleResourceDiff describes the difference between the desired and current state of a resource

type ValidationErrorDetail

type ValidationErrorDetail struct {
	Status  int    `json:"severity"`
	Message string `json:"message"`
}

ValidationErrorDetail is the structure of the document from /managed/delivery-configs/validate API

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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