awscdklambdagoalpha

package module
v2.3.0-alpha.0 Latest Latest
Warning

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

Go to latest
Published: Dec 22, 2021 License: Apache-2.0 Imports: 14 Imported by: 17

README

Amazon Lambda Golang Library


The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the Semantic Versioning model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package.


This library provides constructs for Golang Lambda functions.

To use this module you will either need to have Go installed (go1.11 or later) or Docker installed. See Local Bundling/Docker Bundling for more information.

This module also requires that your Golang application is using a Go version >= 1.11 and is using Go modules.

Go Function

Define a GoFunction:

new lambda.GoFunction(this, 'handler', {
  entry: 'app/cmd/api',
});

By default, if entry points to a directory, then the construct will assume there is a Go entry file (i.e. main.go). Let's look at an example Go project:

lamda-app
├── cmd
│   └── api
│       └── main.go
├── go.mod
├── go.sum
├── pkg
│   ├── auth
│   │   └── auth.go
│   └── middleware
│       └── middleware.go
└── vendor
    ├── github.com
    │   └── aws
    │       └── aws-lambda-go
    └── modules.txt

With the above layout I could either provide the entry as lambda-app/cmd/api or lambda-app/cmd/api/main.go, either will work. When the construct builds the golang binary this will be translated go build ./cmd/api & go build ./cmd/api/main.go respectively. The construct will figure out where it needs to run the go build command from, in this example it would be from the lambda-app directory. It does this by determining the mod file path, which is explained in the next section.

mod file path

The GoFunction tries to automatically determine your project root, that is the root of your golang project. This is usually where the top level go.mod file or vendor folder of your project is located. When bundling in a Docker container, the moduleDir is used as the source (/asset-input) for the volume mounted in the container.

The CDK will walk up parent folders starting from the current working directory until it finds a folder containing a go.mod file.

Alternatively, you can specify the moduleDir prop manually. In this case you need to ensure that this path includes entry and any module/dependencies used by your function. Otherwise bundling will fail.

Runtime

The GoFunction can be used with either the GO_1_X runtime or the provided runtimes (PROVIDED/PROVIDED_AL2). By default it will use the PROVIDED_AL2 runtime. The GO_1_X runtime does not support things like Lambda Extensions, whereas the provided runtimes do. The aws-lambda-go library has built in support for the provided runtime as long as you name the handler bootstrap (which we do by default).

Dependencies

The construct will attempt to figure out how to handle the dependencies for your function. It will do this by determining whether or not you are vendoring your dependencies. It makes this determination by looking to see if there is a vendor folder at the mod file path.

With this information the construct can determine what commands to run. You will generally fall into two scenarios:

  1. You are using vendoring (indicated by the presence of a vendor folder) In this case go build will be run with -mod=vendor set
  2. You are not using vendoring (indicated by the absence of a vendor folder) If you are not vendoring then go build will be run without -mod=vendor since the default behavior is to download dependencies

All other properties of lambda.Function are supported, see also the AWS Lambda construct library.

Environment

By default the following environment variables are set for you:

  • GOOS=linux
  • GOARCH: based on the target architecture of the Lambda function
  • GO111MODULE=on

Use the environment prop to define additional environment variables when go runs:

new lambda.GoFunction(this, 'handler', {
  entry: 'app/cmd/api',
  bundling: {
    environment: {
      HELLO: 'WORLD',
    },
  },
});

Local Bundling

If Go is installed locally and the version is >= go1.11 then it will be used to bundle your code in your environment. Otherwise, bundling will happen in a Lambda compatible Docker container with the Docker platform based on the target architecture of the Lambda function.

For macOS the recommended approach is to install Go as Docker volume performance is really poor.

Go can be installed by following the installation docs.

Docker

To force bundling in a docker container even if Go is available in your environment, set the forceDockerBundling prop to true. This is useful if you want to make sure that your function is built in a consistent Lambda compatible environment.

Use the buildArgs prop to pass build arguments when building the bundling image:

new lambda.GoFunction(this, 'handler', {
  entry: 'app/cmd/api',
  bundling: {
    buildArgs: {
      HTTPS_PROXY: 'https://127.0.0.1:3001',
    },
  },
});

Use the bundling.dockerImage prop to use a custom bundling image:

new lambda.GoFunction(this, 'handler', {
  entry: 'app/cmd/api',
  bundling: {
    dockerImage: DockerImage.fromBuild('/path/to/Dockerfile'),
  },
});

Use the bundling.goBuildFlags prop to pass additional build flags to go build:

new lambda.GoFunction(this, 'handler', {
  entry: 'app/cmd/api',
  bundling: {
    goBuildFlags: ['-ldflags "-s -w"'],
  },
});

Command hooks

It is possible to run additional commands by specifying the commandHooks prop:

// This example only available in TypeScript
// Run additional commands on a GoFunction via `commandHooks` property
new lambda.GoFunction(this, 'handler', {
  bundling: {
    commandHooks: {
      // run tests
      beforeBundling(inputDir: string): string[] {
        return ['go test ./cmd/api -v'];
      },
      // ...
    },
  },
});

The following hooks are available:

  • beforeBundling: runs before all bundling commands
  • afterBundling: runs after all bundling commands

They all receive the directory containing the go.mod file (inputDir) and the directory where the bundled asset will be output (outputDir). They must return an array of commands to run. Commands are chained with &&.

The commands will run in the environment in which bundling occurs: inside the container for Docker bundling or on the host OS for local bundling.

Additional considerations

Depending on how you structure your Golang application, you may want to change the assetHashType parameter. By default this parameter is set to AssetHashType.OUTPUT which means that the CDK will calculate the asset hash (and determine whether or not your code has changed) based on the Golang executable that is created.

If you specify AssetHashType.SOURCE, the CDK will calculate the asset hash by looking at the folder that contains your go.mod file. If you are deploying a single Lambda function, or you want to redeploy all of your functions if anything changes, then AssetHashType.SOURCE will probaby work.

For example, if my app looked like this:

lamda-app
├── cmd
│   └── api
│       └── main.go
├── go.mod
├── go.sum
└── pkg
    └── auth
        └── auth.go

With this structure I would provide the entry as cmd/api which means that the CDK will determine that the protect root is lambda-app (it contains the go.mod file). Since I only have a single Lambda function, and any update to files within the lambda-app directory should trigger a new deploy, I could specify AssetHashType.SOURCE.

On the other hand, if I had a project that deployed mmultiple Lambda functions, for example:

lamda-app
├── cmd
│   ├── api
│   │   └── main.go
│   └── anotherApi
│       └── main.go
├── go.mod
├── go.sum
└── pkg
    ├── auth
    │   └── auth.go
    └── middleware
        └── middleware.go

Then I would most likely want AssetHashType.OUTPUT. With OUTPUT the CDK will only recognize changes if the Golang executable has changed, and Go only includes dependencies that are used in the executable. So in this case if cmd/api used the auth & middleware packages, but cmd/anotherApi did not, then an update to auth or middleware would only trigger an update to the cmd/api Lambda Function.

Documentation

Overview

The CDK Construct Library for AWS Lambda in Golang

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GoFunction_ClassifyVersionProperty

func GoFunction_ClassifyVersionProperty(propertyName *string, locked *bool)

Record whether specific properties in the `AWS::Lambda::Function` resource should also be associated to the Version resource.

See 'currentVersion' section in the module README for more details. Experimental.

func GoFunction_FromFunctionArn

func GoFunction_FromFunctionArn(scope constructs.Construct, id *string, functionArn *string) awslambda.IFunction

Import a lambda function into the CDK using its ARN. Experimental.

func GoFunction_FromFunctionAttributes

func GoFunction_FromFunctionAttributes(scope constructs.Construct, id *string, attrs *awslambda.FunctionAttributes) awslambda.IFunction

Creates a Lambda function object which represents a function not defined within this stack. Experimental.

func GoFunction_IsConstruct

func GoFunction_IsConstruct(x interface{}) *bool

Checks if `x` is a construct.

Returns: true if `x` is an object created from a class which extends `Construct`. Deprecated: use `x instanceof Construct` instead

func GoFunction_IsResource

func GoFunction_IsResource(construct constructs.IConstruct) *bool

Check whether the given construct is a Resource. Experimental.

func GoFunction_MetricAll

func GoFunction_MetricAll(metricName *string, props *awscloudwatch.MetricOptions) awscloudwatch.Metric

Return the given named metric for this Lambda. Experimental.

func GoFunction_MetricAllConcurrentExecutions

func GoFunction_MetricAllConcurrentExecutions(props *awscloudwatch.MetricOptions) awscloudwatch.Metric

Metric for the number of concurrent executions across all Lambdas. Experimental.

func GoFunction_MetricAllDuration

func GoFunction_MetricAllDuration(props *awscloudwatch.MetricOptions) awscloudwatch.Metric

Metric for the Duration executing all Lambdas. Experimental.

func GoFunction_MetricAllErrors

func GoFunction_MetricAllErrors(props *awscloudwatch.MetricOptions) awscloudwatch.Metric

Metric for the number of Errors executing all Lambdas. Experimental.

func GoFunction_MetricAllInvocations

func GoFunction_MetricAllInvocations(props *awscloudwatch.MetricOptions) awscloudwatch.Metric

Metric for the number of invocations of all Lambdas. Experimental.

func GoFunction_MetricAllThrottles

func GoFunction_MetricAllThrottles(props *awscloudwatch.MetricOptions) awscloudwatch.Metric

Metric for the number of throttled invocations of all Lambdas. Experimental.

func GoFunction_MetricAllUnreservedConcurrentExecutions

func GoFunction_MetricAllUnreservedConcurrentExecutions(props *awscloudwatch.MetricOptions) awscloudwatch.Metric

Metric for the number of unreserved concurrent executions across all Lambdas. Experimental.

func NewGoFunction_Override

func NewGoFunction_Override(g GoFunction, scope constructs.Construct, id *string, props *GoFunctionProps)

Experimental.

Types

type BundlingOptions

type BundlingOptions struct {
	// Specify a custom hash for this asset.
	//
	// If `assetHashType` is set it must
	// be set to `AssetHashType.CUSTOM`. For consistency, this custom hash will
	// be SHA256 hashed and encoded as hex. The resulting hash will be the asset
	// hash.
	//
	// NOTE: the hash is used in order to identify a specific revision of the asset, and
	// used for optimizing and caching deployment activities related to this asset such as
	// packaging, uploading to Amazon S3, etc. If you chose to customize the hash, you will
	// need to make sure it is updated every time the asset changes, or otherwise it is
	// possible that some deployments will not be invalidated.
	// Experimental.
	AssetHash *string `json:"assetHash"`
	// Determines how the asset hash is calculated. Assets will get rebuilt and uploaded only if their hash has changed.
	//
	// If the asset hash is set to `OUTPUT` (default), the hash is calculated
	// after bundling. This means that any change in the output will cause
	// the asset to be invalidated and uploaded. Bear in mind that the
	// go binary that is output can be different depending on the environment
	// that it was compiled in. If you want to control when the output is changed
	// it is recommended that you use immutable build images such as
	// `public.ecr.aws/bitnami/golang:1.16.3-debian-10-r16`.
	//
	// If the asset hash is set to `SOURCE`, then only changes to the source
	// directory will cause the asset to rebuild. If your go project has multiple
	// Lambda functions this means that an update to any one function could cause
	// all the functions to be rebuilt and uploaded.
	// Experimental.
	AssetHashType awscdk.AssetHashType `json:"assetHashType"`
	// Build arguments to pass when building the bundling image.
	// Experimental.
	BuildArgs *map[string]*string `json:"buildArgs"`
	// Whether or not to enable cgo during go build.
	//
	// This will set the CGO_ENABLED environment variable
	// Experimental.
	CgoEnabled *bool `json:"cgoEnabled"`
	// Command hooks.
	// Experimental.
	CommandHooks ICommandHooks `json:"commandHooks"`
	// A custom bundling Docker image.
	// Experimental.
	DockerImage awscdk.DockerImage `json:"dockerImage"`
	// Environment variables defined when go runs.
	// Experimental.
	Environment *map[string]*string `json:"environment"`
	// Force bundling in a Docker container even if local bundling is possible.
	// Experimental.
	ForcedDockerBundling *bool `json:"forcedDockerBundling"`
	// List of additional flags to use while building.
	//
	// For example:
	// ['ldflags "-s -w"']
	// Experimental.
	GoBuildFlags *[]*string `json:"goBuildFlags"`
}

Bundling options.

TODO: EXAMPLE

Experimental.

type GoFunction

type GoFunction interface {
	awslambda.Function
	Architecture() awslambda.Architecture
	CanCreatePermissions() *bool
	Connections() awsec2.Connections
	CurrentVersion() awslambda.Version
	DeadLetterQueue() awssqs.IQueue
	Env() *awscdk.ResourceEnvironment
	FunctionArn() *string
	FunctionName() *string
	GrantPrincipal() awsiam.IPrincipal
	IsBoundToVpc() *bool
	LatestVersion() awslambda.IVersion
	LogGroup() awslogs.ILogGroup
	Node() constructs.Node
	PermissionsNode() constructs.Node
	PhysicalName() *string
	Role() awsiam.IRole
	Runtime() awslambda.Runtime
	Stack() awscdk.Stack
	Timeout() awscdk.Duration
	AddEnvironment(key *string, value *string, options *awslambda.EnvironmentOptions) awslambda.Function
	AddEventSource(source awslambda.IEventSource)
	AddEventSourceMapping(id *string, options *awslambda.EventSourceMappingOptions) awslambda.EventSourceMapping
	AddLayers(layers ...awslambda.ILayerVersion)
	AddPermission(id *string, permission *awslambda.Permission)
	AddToRolePolicy(statement awsiam.PolicyStatement)
	ApplyRemovalPolicy(policy awscdk.RemovalPolicy)
	ConfigureAsyncInvoke(options *awslambda.EventInvokeConfigOptions)
	GeneratePhysicalName() *string
	GetResourceArnAttribute(arnAttr *string, arnComponents *awscdk.ArnComponents) *string
	GetResourceNameAttribute(nameAttr *string) *string
	GrantInvoke(grantee awsiam.IGrantable) awsiam.Grant
	Metric(metricName *string, props *awscloudwatch.MetricOptions) awscloudwatch.Metric
	MetricDuration(props *awscloudwatch.MetricOptions) awscloudwatch.Metric
	MetricErrors(props *awscloudwatch.MetricOptions) awscloudwatch.Metric
	MetricInvocations(props *awscloudwatch.MetricOptions) awscloudwatch.Metric
	MetricThrottles(props *awscloudwatch.MetricOptions) awscloudwatch.Metric
	ToString() *string
}

A Golang Lambda function.

TODO: EXAMPLE

Experimental.

func NewGoFunction

func NewGoFunction(scope constructs.Construct, id *string, props *GoFunctionProps) GoFunction

Experimental.

type GoFunctionProps

type GoFunctionProps struct {
	// The maximum age of a request that Lambda sends to a function for processing.
	//
	// Minimum: 60 seconds
	// Maximum: 6 hours
	// Experimental.
	MaxEventAge awscdk.Duration `json:"maxEventAge"`
	// The destination for failed invocations.
	// Experimental.
	OnFailure awslambda.IDestination `json:"onFailure"`
	// The destination for successful invocations.
	// Experimental.
	OnSuccess awslambda.IDestination `json:"onSuccess"`
	// The maximum number of times to retry when the function returns an error.
	//
	// Minimum: 0
	// Maximum: 2
	// Experimental.
	RetryAttempts *float64 `json:"retryAttempts"`
	// Whether to allow the Lambda to send all network traffic.
	//
	// If set to false, you must individually add traffic rules to allow the
	// Lambda to connect to network targets.
	// Experimental.
	AllowAllOutbound *bool `json:"allowAllOutbound"`
	// Lambda Functions in a public subnet can NOT access the internet.
	//
	// Use this property to acknowledge this limitation and still place the function in a public subnet.
	// See: https://stackoverflow.com/questions/52992085/why-cant-an-aws-lambda-function-inside-a-public-subnet-in-a-vpc-connect-to-the/52994841#52994841
	//
	// Experimental.
	AllowPublicSubnet *bool `json:"allowPublicSubnet"`
	// The system architectures compatible with this lambda function.
	// Experimental.
	Architecture awslambda.Architecture `json:"architecture"`
	// Code signing config associated with this function.
	// Experimental.
	CodeSigningConfig awslambda.ICodeSigningConfig `json:"codeSigningConfig"`
	// Options for the `lambda.Version` resource automatically created by the `fn.currentVersion` method.
	// Experimental.
	CurrentVersionOptions *awslambda.VersionOptions `json:"currentVersionOptions"`
	// The SQS queue to use if DLQ is enabled.
	// Experimental.
	DeadLetterQueue awssqs.IQueue `json:"deadLetterQueue"`
	// Enabled DLQ.
	//
	// If `deadLetterQueue` is undefined,
	// an SQS queue with default options will be defined for your Function.
	// Experimental.
	DeadLetterQueueEnabled *bool `json:"deadLetterQueueEnabled"`
	// A description of the function.
	// Experimental.
	Description *string `json:"description"`
	// Key-value pairs that Lambda caches and makes available for your Lambda functions.
	//
	// Use environment variables to apply configuration changes, such
	// as test and production environment configurations, without changing your
	// Lambda function source code.
	// Experimental.
	Environment *map[string]*string `json:"environment"`
	// The AWS KMS key that's used to encrypt your function's environment variables.
	// Experimental.
	EnvironmentEncryption awskms.IKey `json:"environmentEncryption"`
	// Event sources for this function.
	//
	// You can also add event sources using `addEventSource`.
	// Experimental.
	Events *[]awslambda.IEventSource `json:"events"`
	// The filesystem configuration for the lambda function.
	// Experimental.
	Filesystem awslambda.FileSystem `json:"filesystem"`
	// A name for the function.
	// Experimental.
	FunctionName *string `json:"functionName"`
	// Initial policy statements to add to the created Lambda Role.
	//
	// You can call `addToRolePolicy` to the created lambda to add statements post creation.
	// Experimental.
	InitialPolicy *[]awsiam.PolicyStatement `json:"initialPolicy"`
	// Specify the version of CloudWatch Lambda insights to use for monitoring.
	// See: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Lambda-Insights-Getting-Started-docker.html
	//
	// Experimental.
	InsightsVersion awslambda.LambdaInsightsVersion `json:"insightsVersion"`
	// A list of layers to add to the function's execution environment.
	//
	// You can configure your Lambda function to pull in
	// additional code during initialization in the form of layers. Layers are packages of libraries or other dependencies
	// that can be used by multiple functions.
	// Experimental.
	Layers *[]awslambda.ILayerVersion `json:"layers"`
	// The number of days log events are kept in CloudWatch Logs.
	//
	// When updating
	// this property, unsetting it doesn't remove the log retention policy. To
	// remove the retention policy, set the value to `INFINITE`.
	// Experimental.
	LogRetention awslogs.RetentionDays `json:"logRetention"`
	// When log retention is specified, a custom resource attempts to create the CloudWatch log group.
	//
	// These options control the retry policy when interacting with CloudWatch APIs.
	// Experimental.
	LogRetentionRetryOptions *awslambda.LogRetentionRetryOptions `json:"logRetentionRetryOptions"`
	// The IAM role for the Lambda function associated with the custom resource that sets the retention policy.
	// Experimental.
	LogRetentionRole awsiam.IRole `json:"logRetentionRole"`
	// The amount of memory, in MB, that is allocated to your Lambda function.
	//
	// Lambda uses this value to proportionally allocate the amount of CPU
	// power. For more information, see Resource Model in the AWS Lambda
	// Developer Guide.
	// Experimental.
	MemorySize *float64 `json:"memorySize"`
	// Enable profiling.
	// See: https://docs.aws.amazon.com/codeguru/latest/profiler-ug/setting-up-lambda.html
	//
	// Experimental.
	Profiling *bool `json:"profiling"`
	// Profiling Group.
	// See: https://docs.aws.amazon.com/codeguru/latest/profiler-ug/setting-up-lambda.html
	//
	// Experimental.
	ProfilingGroup awscodeguruprofiler.IProfilingGroup `json:"profilingGroup"`
	// The maximum of concurrent executions you want to reserve for the function.
	// See: https://docs.aws.amazon.com/lambda/latest/dg/concurrent-executions.html
	//
	// Experimental.
	ReservedConcurrentExecutions *float64 `json:"reservedConcurrentExecutions"`
	// Lambda execution role.
	//
	// This is the role that will be assumed by the function upon execution.
	// It controls the permissions that the function will have. The Role must
	// be assumable by the 'lambda.amazonaws.com' service principal.
	//
	// The default Role automatically has permissions granted for Lambda execution. If you
	// provide a Role, you must add the relevant AWS managed policies yourself.
	//
	// The relevant managed policies are "service-role/AWSLambdaBasicExecutionRole" and
	// "service-role/AWSLambdaVPCAccessExecutionRole".
	// Experimental.
	Role awsiam.IRole `json:"role"`
	// The list of security groups to associate with the Lambda's network interfaces.
	//
	// Only used if 'vpc' is supplied.
	// Experimental.
	SecurityGroups *[]awsec2.ISecurityGroup `json:"securityGroups"`
	// The function execution time (in seconds) after which Lambda terminates the function.
	//
	// Because the execution time affects cost, set this value
	// based on the function's expected execution time.
	// Experimental.
	Timeout awscdk.Duration `json:"timeout"`
	// Enable AWS X-Ray Tracing for Lambda Function.
	// Experimental.
	Tracing awslambda.Tracing `json:"tracing"`
	// VPC network to place Lambda network interfaces.
	//
	// Specify this if the Lambda function needs to access resources in a VPC.
	// Experimental.
	Vpc awsec2.IVpc `json:"vpc"`
	// Where to place the network interfaces within the VPC.
	//
	// Only used if 'vpc' is supplied. Note: internet access for Lambdas
	// requires a NAT gateway, so picking Public subnets is not allowed.
	// Experimental.
	VpcSubnets *awsec2.SubnetSelection `json:"vpcSubnets"`
	// The path to the folder or file that contains the main application entry point files for the project.
	//
	// This accepts either a path to a directory or file.
	//
	// If a directory path is provided then it will assume there is a Go entry file (i.e. `main.go`) and
	// will construct the build command using the directory path.
	//
	// For example, if you provide the entry as:
	//
	//      entry: 'my-lambda-app/cmd/api'
	//
	// Then the `go build` command would be:
	//
	//      `go build ./cmd/api`
	//
	// If a path to a file is provided then it will use the filepath in the build command.
	//
	// For example, if you provide the entry as:
	//
	//      entry: 'my-lambda-app/cmd/api/main.go'
	//
	// Then the `go build` command would be:
	//
	//      `go build ./cmd/api/main.go`
	// Experimental.
	Entry *string `json:"entry"`
	// Bundling options.
	// Experimental.
	Bundling *BundlingOptions `json:"bundling"`
	// Directory containing your go.mod file.
	//
	// This will accept either a directory path containing a `go.mod` file
	// or a filepath to your `go.mod` file (i.e. `path/to/go.mod`).
	//
	// This will be used as the source of the volume mounted in the Docker
	// container and will be the directory where it will run `go build` from.
	// Experimental.
	ModuleDir *string `json:"moduleDir"`
	// The runtime environment.
	//
	// Only runtimes of the Golang family and provided family are supported.
	// Experimental.
	Runtime awslambda.Runtime `json:"runtime"`
}

Properties for a GolangFunction.

TODO: EXAMPLE

Experimental.

type ICommandHooks

type ICommandHooks interface {
	// Returns commands to run after bundling.
	//
	// Commands are chained with `&&`.
	// Experimental.
	AfterBundling(inputDir *string, outputDir *string) *[]*string
	// Returns commands to run before bundling.
	//
	// Commands are chained with `&&`.
	// Experimental.
	BeforeBundling(inputDir *string, outputDir *string) *[]*string
}

Command hooks.

These commands will run in the environment in which bundling occurs: inside the container for Docker bundling or on the host OS for local bundling.

Commands are chained with `&&`.

```text

{
   // Run tests prior to bundling
   beforeBundling(inputDir: string, outputDir: string): string[] {
     return [`go test -mod=vendor ./...`];
   }
   // ...
}

``` Experimental.

Directories

Path Synopsis
Package jsii contains the functionaility needed for jsii packages to initialize their dependencies and themselves.
Package jsii contains the functionaility needed for jsii packages to initialize their dependencies and themselves.

Jump to

Keyboard shortcuts

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