hcsshim

package module
v0.0.0-...-2393a11 Latest Latest
Warning

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

Go to latest
Published: Jan 13, 2021 License: MIT Imports: 16 Imported by: 0

README

hcsshim

Build status

This package contains the Golang interface for using the Windows Host Compute Service (HCS) to launch and manage Windows Containers. It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS).

It is primarily used in the Moby Project, but it can be freely used by other projects as well.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

We also ask that contributors sign their commits using git commit -s or git commit --signoff to certify they either authored the work themselves or otherwise have permission to use it in this project.

Code of Conduct

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

Dependencies

This project requires Golang 1.9 or newer to build.

For system requirements to run this project, see the Microsoft docs on Windows Container requirements.

Reporting Security Issues

Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) at secure@microsoft.com. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the Security TechCenter.

For additional details, see Report a Computer Security Vulnerability on Technet


Copyright (c) 2018 Microsoft Corp. All rights reserved.

Documentation

Index

Constants

View Source
const (
	Add     = schema1.Add
	Remove  = schema1.Remove
	Network = schema1.Network
)

RequestType const

View Source
const (
	// Specific user-visible exit codes
	WaitErrExecFailed = 32767

	ERROR_GEN_FAILURE          = hcserror.ERROR_GEN_FAILURE
	ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115)
	WSAEINVAL                  = syscall.Errno(10022)

	// Timeout on wait calls
	TimeoutInfinite = 0xFFFFFFFF
)
View Source
const (
	Nat                  = hns.Nat
	ACL                  = hns.ACL
	PA                   = hns.PA
	VLAN                 = hns.VLAN
	VSID                 = hns.VSID
	VNet                 = hns.VNet
	L2Driver             = hns.L2Driver
	Isolation            = hns.Isolation
	QOS                  = hns.QOS
	OutboundNat          = hns.OutboundNat
	ExternalLoadBalancer = hns.ExternalLoadBalancer
	Route                = hns.Route
	Proxy                = hns.Proxy
)

RequestType const

View Source
const (
	Allow = hns.Allow
	Block = hns.Block

	In  = hns.In
	Out = hns.Out

	Host   = hns.Host
	Switch = hns.Switch
)

Variables

View Source
var (
	// ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists = hcs.exist
	ErrComputeSystemDoesNotExist = hcs.ErrComputeSystemDoesNotExist

	// ErrElementNotFound is an error encountered when the object being referenced does not exist
	ErrElementNotFound = hcs.ErrElementNotFound

	// ErrElementNotFound is an error encountered when the object being referenced does not exist
	ErrNotSupported = hcs.ErrNotSupported

	// ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
	// decimal -2147024883 / hex 0x8007000d
	ErrInvalidData = hcs.ErrInvalidData

	// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
	ErrHandleClose = hcs.ErrHandleClose

	// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
	ErrAlreadyClosed = hcs.ErrAlreadyClosed

	// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
	ErrInvalidNotificationType = hcs.ErrInvalidNotificationType

	// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
	ErrInvalidProcessState = hcs.ErrInvalidProcessState

	// ErrTimeout is an error encountered when waiting on a notification times out
	ErrTimeout = hcs.ErrTimeout

	// ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
	// a different expected notification
	ErrUnexpectedContainerExit = hcs.ErrUnexpectedContainerExit

	// ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
	// is lost while waiting for a notification
	ErrUnexpectedProcessAbort = hcs.ErrUnexpectedProcessAbort

	// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
	ErrUnexpectedValue = hcs.ErrUnexpectedValue

	// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
	ErrVmcomputeAlreadyStopped = hcs.ErrVmcomputeAlreadyStopped

	// ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
	ErrVmcomputeOperationPending = hcs.ErrVmcomputeOperationPending

	// ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
	ErrVmcomputeOperationInvalidState = hcs.ErrVmcomputeOperationInvalidState

	// ErrProcNotFound is an error encountered when the the process cannot be found
	ErrProcNotFound = hcs.ErrProcNotFound

	// ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
	// builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
	ErrVmcomputeOperationAccessIsDenied = hcs.ErrVmcomputeOperationAccessIsDenied

	// ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
	ErrVmcomputeInvalidJSON = hcs.ErrVmcomputeInvalidJSON

	// ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
	ErrVmcomputeUnknownMessage = hcs.ErrVmcomputeUnknownMessage

	// ErrNotSupported is an error encountered when hcs doesn't support the request
	ErrPlatformNotSupported = hcs.ErrPlatformNotSupported
)
View Source
var (
	HNSVersion1803 = hns.HNSVersion1803
)

Functions

func ActivateLayer

func ActivateLayer(info DriverInfo, id string) error

func CreateLayer

func CreateLayer(info DriverInfo, id, parent string) error

func CreateSandboxLayer

func CreateSandboxLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error

New clients should use CreateScratchLayer instead. Kept in to preserve API compatibility.

func CreateScratchLayer

func CreateScratchLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error

func DeactivateLayer

func DeactivateLayer(info DriverInfo, id string) error

func DestroyLayer

func DestroyLayer(info DriverInfo, id string) error

func ExpandSandboxSize

func ExpandSandboxSize(info DriverInfo, layerId string, size uint64) error

New clients should use ExpandScratchSize instead. Kept in to preserve API compatibility.

func ExpandScratchSize

func ExpandScratchSize(info DriverInfo, layerId string, size uint64) error

func ExportLayer

func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error

func GetLayerMountPath

func GetLayerMountPath(info DriverInfo, id string) (string, error)

func GetSharedBaseImages

func GetSharedBaseImages() (imageData string, err error)

func HotAttachEndpoint

func HotAttachEndpoint(containerID string, endpointID string) error

HotAttachEndpoint makes a HCS Call to attach the endpoint to the container

func HotDetachEndpoint

func HotDetachEndpoint(containerID string, endpointID string) error

HotDetachEndpoint makes a HCS Call to detach the endpoint from the container

func ImportLayer

func ImportLayer(info DriverInfo, layerID string, importFolderPath string, parentLayerPaths []string) error

func IsAlreadyClosed

func IsAlreadyClosed(err error) bool

IsAlreadyClosed checks if an error is caused by the Container or Process having been already closed by a call to the Close() method.

func IsAlreadyStopped

func IsAlreadyStopped(err error) bool

IsAlreadyStopped returns a boolean indicating whether the error is caused by a Container or Process being already stopped. Note: Currently, ErrElementNotFound can mean that a Process has either already exited, or does not exist. Both IsAlreadyStopped and IsNotExist will currently return true when the error is ErrElementNotFound or ErrProcNotFound.

func IsNotExist

func IsNotExist(err error) bool

IsNotExist checks if an error is caused by the Container or Process not existing. Note: Currently, ErrElementNotFound can mean that a Process has either already exited, or does not exist. Both IsAlreadyStopped and IsNotExist will currently return true when the error is ErrElementNotFound or ErrProcNotFound.

func IsNotSupported

func IsNotSupported(err error) bool

IsNotSupported returns a boolean indicating whether the error is caused by unsupported platform requests Note: Currently Unsupported platform requests can be mean either ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage is thrown from the Platform

func IsPending

func IsPending(err error) bool

IsPending returns a boolean indicating whether the error is that the requested operation is being completed in the background.

func IsTimeout

func IsTimeout(err error) bool

IsTimeout returns a boolean indicating whether the error is caused by a timeout waiting for the operation to complete.

func LayerExists

func LayerExists(info DriverInfo, id string) (bool, error)

func PrepareLayer

func PrepareLayer(info DriverInfo, layerId string, parentLayerPaths []string) error

func ProcessBaseLayer

func ProcessBaseLayer(path string) error

func ProcessUtilityVMImage

func ProcessUtilityVMImage(path string) error

func UnprepareLayer

func UnprepareLayer(info DriverInfo, layerId string) error

Types

type ACLPolicy

type ACLPolicy = hns.ACLPolicy

type ActionType

type ActionType = hns.ActionType

type AssignedDevice

type AssignedDevice = schema1.AssignedDevice

AssignedDevice represents a device that has been directly assigned to a container

NOTE: Support added in RS5

type ComputeSystemQuery

type ComputeSystemQuery = schema1.ComputeSystemQuery

type Container

type Container interface {
	// Start synchronously starts the container.
	Start() error

	// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
	Shutdown() error

	// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
	Terminate() error

	// Waits synchronously waits for the container to shutdown or terminate.
	Wait() error

	// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
	// returns false if timeout occurs.
	WaitTimeout(time.Duration) error

	// Pause pauses the execution of a container.
	Pause() error

	// Resume resumes the execution of a container.
	Resume() error

	// HasPendingUpdates returns true if the container has updates pending to install.
	HasPendingUpdates() (bool, error)

	// Statistics returns statistics for a container.
	Statistics() (Statistics, error)

	// ProcessList returns details for the processes in a container.
	ProcessList() ([]ProcessListItem, error)

	// MappedVirtualDisks returns virtual disks mapped to a utility VM, indexed by controller
	MappedVirtualDisks() (map[int]MappedVirtualDiskController, error)

	// CreateProcess launches a new process within the container.
	CreateProcess(c *ProcessConfig) (Process, error)

	// OpenProcess gets an interface to an existing process within the container.
	OpenProcess(pid int) (Process, error)

	// Close cleans up any state associated with the container but does not terminate or wait for it.
	Close() error

	// Modify the System
	Modify(config *ResourceModificationRequestResponse) error
}

Container represents a created (but not necessarily running) container.

func CreateContainer

func CreateContainer(id string, c *ContainerConfig) (Container, error)

CreateContainer creates a new container with the given configuration but does not start it.

func OpenContainer

func OpenContainer(id string) (Container, error)

OpenContainer opens an existing container by ID.

type ContainerConfig

type ContainerConfig = schema1.ContainerConfig

ContainerConfig is used as both the input of CreateContainer and to convert the parameters to JSON for passing onto the HCS

type ContainerError

type ContainerError struct {
	Container *container
	Operation string
	ExtraInfo string
	Err       error
	Events    []hcs.ErrorEvent
}

ContainerError is an error encountered in HCS during an operation on a Container object

func (*ContainerError) Error

func (e *ContainerError) Error() string

type ContainerProperties

type ContainerProperties = schema1.ContainerProperties

ContainerProperties holds the properties for a container and the processes running in that container

func GetContainers

func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error)

GetContainers gets a list of the containers on the system that match the query

type DirectionType

type DirectionType = hns.DirectionType

type DriverInfo

type DriverInfo struct {
	Flavour int
	HomeDir string
}

type ELBPolicy

type ELBPolicy = hns.ELBPolicy

ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy

type EndpointAttachDetachRequest

type EndpointAttachDetachRequest = hns.EndpointAttachDetachRequest

EndpointAttachDetachRequest is the structure used to send request to the container to modify the system Supported resource types are Network and Request Types are Add/Remove

type EndpointNotFoundError

type EndpointNotFoundError = hns.EndpointNotFoundError

type EndpointResquestResponse

type EndpointResquestResponse = hns.EndpointResquestResponse

EndpointResquestResponse is object to get the endpoint request response

type GUID

type GUID [16]byte

func NameToGuid

func NameToGuid(name string) (id GUID, err error)

func NewGUID

func NewGUID(source string) *GUID

func (*GUID) ToString

func (g *GUID) ToString() string

type HNSAclFeatures

type HNSAclFeatures = hns.HNSAclFeatures

type HNSEndpoint

type HNSEndpoint = hns.HNSEndpoint

HNSEndpoint represents a network endpoint in HNS

func GetHNSEndpointByID

func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error)

GetHNSEndpointByID get the Endpoint by ID

func GetHNSEndpointByName

func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error)

GetHNSEndpointByName gets the endpoint filtered by Name

func HNSEndpointRequest

func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error)

HNSEndpointRequest makes a HNS call to modify/query a network endpoint

func HNSListEndpointRequest

func HNSListEndpointRequest() ([]HNSEndpoint, error)

HNSListEndpointRequest makes a HNS call to query the list of available endpoints

type HNSGlobals

type HNSGlobals = hns.HNSGlobals

func GetHNSGlobals

func GetHNSGlobals() (*HNSGlobals, error)

type HNSNetwork

type HNSNetwork = hns.HNSNetwork

HNSNetwork represents a network in HNS

func GetHNSNetworkByID

func GetHNSNetworkByID(networkID string) (*HNSNetwork, error)

GetHNSNetworkByID

func GetHNSNetworkByName

func GetHNSNetworkByName(networkName string) (*HNSNetwork, error)

GetHNSNetworkName filtered by Name

func HNSListNetworkRequest

func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error)

HNSListNetworkRequest makes a HNS call to query the list of available networks

func HNSNetworkRequest

func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error)

HNSNetworkRequest makes a call into HNS to update/query a single network

type HNSSupportedFeatures

type HNSSupportedFeatures = hns.HNSSupportedFeatures

func GetHNSSupportedFeatures

func GetHNSSupportedFeatures() HNSSupportedFeatures

type HNSVersion

type HNSVersion = hns.HNSVersion

type HcsError

type HcsError = hcserror.HcsError

type HvRuntime

type HvRuntime = schema1.HvRuntime

type IsolationPolicy

type IsolationPolicy = hns.IsolationPolicy

type LBPolicy

type LBPolicy = hns.LBPolicy

LBPolicy is a structure defining schema for LoadBalancing based Policy

type Layer

type Layer = schema1.Layer

type LayerReader

type LayerReader = wclayer.LayerReader

func NewLayerReader

func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error)

type LayerWriter

type LayerWriter = wclayer.LayerWriter

func NewLayerWriter

func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error)

type MacPool

type MacPool = hns.MacPool

MacPool is assoicated with a network and represents a list of macaddresses available to the network

type MappedDir

type MappedDir = schema1.MappedDir

type MappedPipe

type MappedPipe = schema1.MappedPipe

type MappedVirtualDisk

type MappedVirtualDisk = schema1.MappedVirtualDisk

type MappedVirtualDiskController

type MappedVirtualDiskController = schema1.MappedVirtualDiskController

MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container

type MemoryStats

type MemoryStats = schema1.MemoryStats

MemoryStats holds the memory statistics for a container

type Namespace

type Namespace = hns.Namespace

Namespace represents a Compartment.

type NatPolicy

type NatPolicy = hns.NatPolicy

type NetworkNotFoundError

type NetworkNotFoundError = hns.NetworkNotFoundError

type NetworkStats

type NetworkStats = schema1.NetworkStats

NetworkStats holds the network statistics for a container

type OutboundNatPolicy

type OutboundNatPolicy = hns.OutboundNatPolicy

type PaPolicy

type PaPolicy = hns.PaPolicy

type Policy

type Policy = hns.Policy

type PolicyList

type PolicyList = hns.PolicyList

PolicyList is a structure defining schema for Policy list request

func AddLoadBalancer

func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error)

AddLoadBalancer policy list for the specified endpoints

func AddRoute

func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error)

AddRoute adds route policy list for the specified endpoints

func GetPolicyListByID

func GetPolicyListByID(policyListID string) (*PolicyList, error)

GetPolicyListByID get the policy list by ID

func HNSListPolicyListRequest

func HNSListPolicyListRequest() ([]PolicyList, error)

HNSListPolicyListRequest gets all the policy list

func HNSPolicyListRequest

func HNSPolicyListRequest(method, path, request string) (*PolicyList, error)

HNSPolicyListRequest makes a call into HNS to update/query a single network

func PolicyListRequest

func PolicyListRequest(method, path, request string) (*PolicyList, error)

PolicyListRequest makes a HNS call to modify/query a network policy list

type PolicyType

type PolicyType = hns.PolicyType

Type of Request Support in ModifySystem

type Process

type Process interface {
	// Pid returns the process ID of the process within the container.
	Pid() int

	// Kill signals the process to terminate but does not wait for it to finish terminating.
	Kill() error

	// Wait waits for the process to exit.
	Wait() error

	// WaitTimeout waits for the process to exit or the duration to elapse. It returns
	// false if timeout occurs.
	WaitTimeout(time.Duration) error

	// ExitCode returns the exit code of the process. The process must have
	// already terminated.
	ExitCode() (int, error)

	// ResizeConsole resizes the console of the process.
	ResizeConsole(width, height uint16) error

	// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
	// these pipes does not close the underlying pipes; it should be possible to
	// call this multiple times to get multiple interfaces.
	Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error)

	// CloseStdin closes the write side of the stdin pipe so that the process is
	// notified on the read side that there is no more data in stdin.
	CloseStdin() error

	// Close cleans up any state associated with the process but does not kill
	// or wait on it.
	Close() error
}

Process represents a running or exited process.

type ProcessConfig

type ProcessConfig = schema1.ProcessConfig

ProcessConfig is used as both the input of Container.CreateProcess and to convert the parameters to JSON for passing onto the HCS

type ProcessError

type ProcessError struct {
	Process   *process
	Operation string
	ExtraInfo string
	Err       error
	Events    []hcs.ErrorEvent
}

ProcessError is an error encountered in HCS during an operation on a Process object

func (*ProcessError) Error

func (e *ProcessError) Error() string

type ProcessListItem

type ProcessListItem = schema1.ProcessListItem

ProcessList is the structure of an item returned by a ProcessList call on a container

type ProcessorStats

type ProcessorStats = schema1.ProcessorStats

ProcessorStats holds the processor statistics for a container

type ProxyPolicy

type ProxyPolicy = hns.ProxyPolicy

type QosPolicy

type QosPolicy = hns.QosPolicy

type RequestType

type RequestType = schema1.RequestType

Type of Request Support in ModifySystem

type ResourceModificationRequestResponse

type ResourceModificationRequestResponse = schema1.ResourceModificationRequestResponse

ResourceModificationRequestResponse is the structure used to send request to the container to modify the system Supported resource types are Network and Request Types are Add/Remove

type ResourceType

type ResourceType = schema1.ResourceType

Type of Resource Support in ModifySystem

type RoutePolicy

type RoutePolicy = hns.RoutePolicy

RoutePolicy is a structure defining schema for Route based Policy

type RuleType

type RuleType = hns.RuleType

type Statistics

type Statistics = schema1.Statistics

Statistics is the structure returned by a statistics call on a container

type StorageStats

type StorageStats = schema1.StorageStats

StorageStats holds the storage statistics for a container

type Subnet

type Subnet = hns.Subnet

Subnet is assoicated with a network and represents a list of subnets available to the network

type SystemType

type SystemType string

SystemType represents the type of the system on which actions are done

const (
	ContainerType      SystemType = "Container"
	VirtualMachineType SystemType = "VirtualMachine"
	HostType           SystemType = "Host"
)

SystemType const

type VlanPolicy

type VlanPolicy = hns.VlanPolicy

type VsidPolicy

type VsidPolicy = hns.VsidPolicy

type WC_LAYER_DESCRIPTOR

type WC_LAYER_DESCRIPTOR = wclayer.WC_LAYER_DESCRIPTOR

Directories

Path Synopsis
cmd
Package computestorage is a wrapper around the HCS storage APIs.
Package computestorage is a wrapper around the HCS storage APIs.
ext4
Package hcn is a shim for the Host Compute Networking (HCN) service, which manages networking for Windows Server containers and Hyper-V containers.
Package hcn is a shim for the Host Compute Networking (HCN) service, which manages networking for Windows Server containers and Hyper-V containers.
internal
appargs
Package appargs provides argument validation routines for use with github.com/urfave/cli.
Package appargs provides argument validation routines for use with github.com/urfave/cli.
cmd
Package cmd provides functionality used to execute commands inside of containers or UVMs, and to connect an upstream client to those commands for handling in/out/err IO.
Package cmd provides functionality used to execute commands inside of containers or UVMs, and to connect an upstream client to those commands for handling in/out/err IO.
cni
cow
credentials
Package credentials holds the necessary structs and functions for adding and removing Container Credential Guard instances (shortened to CCG normally) for V2 HCS schema containers.
Package credentials holds the necessary structs and functions for adding and removing Container Credential Guard instances (shortened to CCG normally) for V2 HCS schema containers.
gcs
hcs
hns
layers
Package layers deals with container layer mounting/unmounting for LCOW and WCOW
Package layers deals with container layer mounting/unmounting for LCOW and WCOW
log
oc
oci
ociwclayer
Package ociwclayer provides functions for importing and exporting Windows container layers from and to their OCI tar representation.
Package ociwclayer provides functions for importing and exporting Windows container layers from and to their OCI tar representation.
resources
Package resources handles creating, updating, and releasing resources on a container
Package resources handles creating, updating, and releasing resources on a container
uvm
wclayer
Package wclayer provides bindings to HCS's legacy layer management API and provides a higher level interface around these calls for container layer management.
Package wclayer provides bindings to HCS's legacy layer management API and provides a higher level interface around these calls for container layer management.
winapi
Package winapi contains various low-level bindings to Windows APIs.
Package winapi contains various low-level bindings to Windows APIs.
pkg

Jump to

Keyboard shortcuts

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