v1alpha1

package
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: May 3, 2024 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// TermErr classified as an error that needs to stop the program
	TermErr TerminalError = "Hit Terminal Error: "
	// NonTermErr classified as an error that we can ignore
	NonTermErr NonTerminalError = "Hit Non Terminal Error: "
)
View Source
const (
	// ManifestSyncKind is the kind for ManifestSync resources.
	ManifestSyncKind = "ManifestSync"

	// SourceCommitStrategy indicates the source image should have a tag
	// equal to the source commit tag.
	SourceCommitStrategy Strategy = "sourceCommit"
	// MutableTagStrategy means you should pin to whatever image has that commit.
	MutableTagStrategy Strategy = "mutableTag"

	// UnknownStrategy indicates unknown tag matching strategy.
	UnknownStrategy Strategy = "unknown"

	// LatestTagPrefix means you should look for the most recent image
	// which has that tag as a prefix.
	// TODO(jeremy): Should we get rid of this? This was for Primer specific tagging.
	LatestTagPrefix Strategy = "latestTagPrefix"

	// IncludeRepo is the enum value indicating a repo list is an include list.
	IncludeRepo RepoMatchType = "include"
	// ExcludeRepo is the enum value indicating a repo list is an exclude list.
	ExcludeRepo RepoMatchType = "exclude"

	// PauseAnnotation is the annotation used to pause a sync.
	PauseAnnotation    = "hydros.dev/pauseUntil"
	TakeoverAnnotation = "hydros.dev/takeover"
)
View Source
const (
	// Group for MLP tasks.
	Group = "hydros.dev"
	// Version for tasks.
	Version = "v1alpha1"
)
View Source
const (
	// EcrPolicySyncKind is the kind for EcrPolicySync resources.
	EcrPolicySyncKind = "EcrPolicySync"
)

Variables

View Source
var (
	ImageGVK           = schema.FromAPIVersionAndKind(Group+"/"+Version, "Image")
	ReplicatedImageGVK = schema.FromAPIVersionAndKind(Group+"/"+Version, "ReplicatedImage")
)
View Source
var EcrPolicySyncGVK = schema.GroupVersionKind{
	Group:   Group,
	Version: Version,
	Kind:    EcrPolicySyncKind,
}

EcrPolicySyncGVK is the GVK for EcrPolicySync.

View Source
var (
	GitHubReleaserGVK = schema.FromAPIVersionAndKind(Group+"/"+Version, "GitHubReleaser")
)
View Source
var (
	RepoGVK = Gvk{
		Group:   Group,
		Version: Version,
		Kind:    "RepoConfig",
	}
)

Functions

func IsValid

func IsValid(c *HydrosConfig) (string, bool)

IsValid returns true if the config is valid. For invalid config the string will be a message of validation errors

Types

type ArtifactBuilder added in v0.0.2

type ArtifactBuilder struct {
	// GCB is the configuration to build with GoogleCloud Build
	GCB *GCBConfig `yaml:"gcb,omitempty"`
}

type ConfigSpec

type ConfigSpec struct {

	// InPlaceConfigs configure hydrations that are done in-place. This means the hydrated configurations
	// are checked back into the repository
	InPlaceConfigs []InPlaceConfig `yaml:"inPlaceConfigs"`
}

type EcrPolicySync

type EcrPolicySync struct {
	APIVersion string   `yaml:"apiVersion" yamltags:"required"`
	Kind       string   `yaml:"kind" yamltags:"required"`
	Metadata   Metadata `yaml:"metadata,omitempty"`

	Spec EcrPolicySyncSpec `yaml:"spec,omitempty"`
}

EcrPolicySync continually ensures a set of ECR repos exists and has the specified policy

type EcrPolicySyncSpec

type EcrPolicySyncSpec struct {
	// Policy is the JSON representation of the policy to apply
	Policy string `yaml:"policy,omitempty"`

	// ImageRegistry is the registry in which to apply it.
	ImageRegistry string `yaml:"imageRegistry,omitempty"`

	// ImageRepos is a list of repos to apply the changes to.
	ImageRepos []string `yaml:"imageRepos,omitempty"`
}

EcrPolicySyncSpec spec for the resource to sync ECR policies.

type Function

type Function struct {
	// RepoKey can be source or dest and indicates whether the functions are sourced from the source
	// or dest repo.
	RepoKey string `yaml:"repoKey,omitempty"`
	// Paths is the path to the YAML files or directories to apply.
	Paths []string `yaml:"paths,omitempty"`
}

Function is a list of functions to apply

type GCBConfig added in v0.0.2

type GCBConfig struct {
	// Project is the GCP project to use for building
	Project string `yaml:"project,omitempty"`

	// Timeout is a string understood by time.ParseDuration
	// e.g. 10m
	Timeout string `yaml:"timeout,omitempty"`

	// Bucket where to store the build logs
	Bucket string `yaml:"bucket,omitempty"
`
	// MachineType is optional. If specified its the machine type to use for building.
	// Increasing VCPU can increase build times but also comes with a provisioning
	// delay since they are only started on demand (they are also more expensive).
	// Private pools could potentially fix the delay cost
	// See: https://cloud.google.com/build/docs/optimize-builds/increase-vcpu-for-builds#increase_vcpu_for_default_pools
	// See: https://cloud.google.com/build/pricing
	// See: https://cloud.google.com/build/docs/api/reference/rest/v1/projects.builds#machinetype
	// For values. UNSPECIFIED uses the default value which has 1 CPU
	MachineType string `yaml:"machineType,omitempty"`

	// Dockerfile is the path to the Dockerfile to use for building the image
	// This should be the path inside the context
	Dockerfile string `yaml:"dockerfile,omitempty"`
}

GCBConfig is the configuration for building with GoogleCloud Build

type GitHubAppConfig added in v0.0.3

type GitHubAppConfig struct {
	// AppID is the github ghapp id for the ghapp
	AppID int64 `json:"appID,omitempty" yaml:"appID,omitempty"`

	// PrivateKey is the URI of the private key for the ghapp. This can be a secretmanager URI e.g.
	PrivateKey string `json:"privateKey,omitempty" yaml:"privateKey,omitempty"`
}

GitHubAppConfig specifies the configuration for a GitHub App.

type GitHubReleaser added in v0.0.7

type GitHubReleaser struct {
	APIVersion string             `yaml:"apiVersion" yamltags:"required"`
	Kind       string             `yaml:"kind" yamltags:"required"`
	Metadata   Metadata           `yaml:"metadata,omitempty"`
	Spec       GitHubReleaserSpec `yaml:"spec,omitempty"`
}

GitHubReleaser continuously cuts GitHub releases when conditions are met. It takes care of setting the release notes and the version.

type GitHubReleaserSpec added in v0.0.7

type GitHubReleaserSpec struct {
	Org string `yaml:"org,omitempty"`
	// Repo is the repository to release
	Repo string `yaml:"repo,omitempty"`
}

type GitHubRepo

type GitHubRepo struct {
	// Org that owns the repo.
	Org    string `yaml:"org,omitempty"`
	Repo   string `yaml:"repo,omitempty"`
	Branch string `yaml:"branch,omitempty"`
}

GitHubRepo represents a GitHub repo.

func (*GitHubRepo) IsValid

func (r *GitHubRepo) IsValid() error

IsValid checks if this is a valid resource.

type Gvk added in v0.0.3

type Gvk struct {
	Group   string `json:"group,omitempty" yaml:"group,omitempty"`
	Version string `json:"version,omitempty" yaml:"version,omitempty"`
	Kind    string `json:"kind,omitempty" yaml:"kind,omitempty"`
}

type HydrosConfig

type HydrosConfig struct {
	APIVersion string     `yaml:"apiVersion"`
	Kind       string     `yaml:"kind"`
	Metadata   Metadata   `yaml:"metadata"`
	Spec       ConfigSpec `yaml:"spec"`
}

HydrosConfig is hydros GitHub App configuration. This is the configuration that should be checked into a repository to configure hydros on that repository

type Image added in v0.0.2

type Image struct {
	APIVersion string   `yaml:"apiVersion" yamltags:"required"`
	Kind       string   `yaml:"kind" yamltags:"required"`
	Metadata   Metadata `yaml:"metadata,omitempty"`

	Spec   ImageSpec   `yaml:"spec,omitempty"`
	Status ImageStatus `yaml:"status,omitempty"`
}

Image defines an image to be continuously built

func (*Image) IsValid added in v0.0.3

func (c *Image) IsValid() (string, bool)

IsValid returns true if the config is valid. For invalid config the string will be a message of validation errors

type ImageBuilder

type ImageBuilder struct {
	// Enabled is a boolean indicating whether the image builder is enabled or not
	Enabled bool `yaml:"enabled,omitempty"`
	// Registry is the registry to use with the images.
	Registry string `yaml:"registry,omitempty"`
}

ImageBuilder configures the image builder.

type ImageList

type ImageList struct {
	APIVersion string   `yaml:"apiVersion"`
	Kind       string   `yaml:"kind"`
	Metadata   Metadata `yaml:"metadata,omitempty"`

	Images []string `yaml:"images,omitempty"`
}

ImageList is a list of images

type ImageRepoMatch

type ImageRepoMatch struct {
	Repos []string `yaml:"repos,omitempty"`
	// RepoMatchType indicates whether this is an include or exclude list.
	Type RepoMatchType `yaml:"type,omitempty"`
}

ImageRepoMatch describes how to match repos.

type ImageSource added in v0.0.3

type ImageSource struct {
	// URI is the path of the resource to use as a source
	// This can be a local path or a docker image. If its a local path relative paths will be interpreted
	// relative to the location of the YAML file containing the resource.
	// e.g.us-west1-docker.pkg.dev/some-project/images/hydros/agent
	//
	// Use file:// to specify a local path e.g. file:///path/to/dir. Note the third "/" indicates its an absolute path
	// If its "//" then its a relative path. I'm not sure it makes sense to support relative paths because what
	// would they be relative to?
	// TODO(jeremy): If the tag isn't specified we should look for the same tag at which the new image is being built
	URI      string           `yaml:"uri,omitempty"`
	Mappings []*SourceMapping `yaml:"mappings,omitempty"`
}

type ImageSpec added in v0.0.2

type ImageSpec struct {
	// Image is the full path of the image to be built
	// e.g.us-west1-docker.pkg.dev/some-project/images/hydros
	// So it includes the registry and repository but not the tag or digest
	Image string `yaml:"image,omitempty"`
	// Source are the source for the image
	Source  []*ImageSource   `yaml:"source,omitempty"`
	Builder *ArtifactBuilder `yaml:"builder,omitempty"`
}

type ImageStatus added in v0.0.2

type ImageStatus struct {
	// SourceCommit is the commit hash of the source code
	SourceCommit string `yaml:"sourceCommit,omitempty"`
	// URI is the URI of the image
	URI string `yaml:"uri,omitempty"`
	// SHA is the SHA of the image
	SHA string `yaml:"sha,omitempty"`
}

type ImageTagToPin

type ImageTagToPin struct {
	// Tags of the images to look for to pin.
	Tags []string `yaml:"tags,omitempty"`
	// Strategy is an enum indicating how the image should be pinned.
	Strategy Strategy `yaml:"strategy,omitempty"`

	// ImageRepoMatch describes the image repos to match
	// If nil all repos are matched.
	ImageRepoMatch *ImageRepoMatch `yaml:"imageRepoMatch,omitempty"`
}

ImageTagToPin describes an image tag to pin.

type InPlaceConfig

type InPlaceConfig struct {
	// BaseBranch is the branch to use as the base for the hydration.
	// This will be the branch that is checked out and updated by Hydros
	BaseBranch string `yaml:"baseBranch"`
	// PRBranch is the branch hydros will use to prepare the changes
	PRBranch string `yaml:"prBranch"`
	// AutoMerge determines whether Hydros should try to automatically merge the PR.
	// If AutoMerge is true then Hydros will try to enable GitHub AutoMerge on the PR if it is available
	// or it will try to merge the PR if it is immediately mergeable.
	AutoMerge bool `yaml:"autoMerge"`
	// Paths is the relative paths of the directories to search for KRMFunctions
	// If this is blank then the entire repo will be search.
	Paths []string `yaml:"paths"`
}

type LabelSelector

type LabelSelector struct {
	// matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
	// map is equivalent to an element of matchExpressions, whose key field is "key", the
	// operator is "In", and the values array contains only "value". The requirements are ANDed.
	// +optional
	MatchLabels map[string]string `json:"matchLabels,omitempty" yaml:"matchLabels,omitempty"`
	// MatchExpressions is a list of label selector requirements. The requirements are ANDed.
	// +optional
	MatchExpressions []LabelSelectorRequirement `json:"matchExpressions,omitempty" yaml:"matchExpressions,omitempty"`
}

LabelSelector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. +structType=atomic

N.B. Based on https://github.com/kubernetes/apimachinery/blob/460d10991a520527026863efae34f49f89c2f4e1/pkg/apis/meta/v1/types.go#L1096 We copy the struct so we can add YAML annotations

func (*LabelSelector) ToK8s

func (s *LabelSelector) ToK8s() (*meta.LabelSelector, error)

ToK8s converts it to a K8s Label selector. This indirection is necessary because we need to change how the LabelSelector is serialized to YAML by adding YAML tags.

type LabelSelectorOperator

type LabelSelectorOperator string

LabelSelectorOperator is the set of operators that can be used in a selector requirement.

const (
	// LabelSelectorOpIn is the in operator
	LabelSelectorOpIn LabelSelectorOperator = "In"
	// LabelSelectorOpNotIn is the not in operator
	LabelSelectorOpNotIn LabelSelectorOperator = "NotIn"
	// LabelSelectorOpExists is the exists operator
	LabelSelectorOpExists LabelSelectorOperator = "Exists"
	// LabelSelectorOpDoesNotExist is the does not exist operator
	LabelSelectorOpDoesNotExist LabelSelectorOperator = "DoesNotExist"
)

type LabelSelectorRequirement

type LabelSelectorRequirement struct {
	// key is the label key that the selector applies to.
	// +patchMergeKey=key
	// +patchStrategy=merge
	Key string `json:"key" yaml:"key" patchStrategy:"merge" patchMergeKey:"key"`
	// operator represents a key's relationship to a set of values.
	// Valid operators are In, NotIn, Exists and DoesNotExist.
	Operator LabelSelectorOperator `json:"operator" yaml:"operator"`
	// values is an array of string values. If the operator is In or NotIn,
	// the values array must be non-empty. If the operator is Exists or DoesNotExist,
	// the values array must be empty. This array is replaced during a strategic
	// merge patch.
	// +optional
	Values []string `json:"values,omitempty" yaml:"values,omitempty"`
}

LabelSelectorRequirement is a selector that contains values, a key, and an operator that relates the key and values.

type ManifestSync

type ManifestSync struct {
	APIVersion string   `yaml:"apiVersion" yamltags:"required"`
	Kind       string   `yaml:"kind" yamltags:"required"`
	Metadata   Metadata `yaml:"metadata,omitempty"`

	Spec   ManifestSyncSpec   `yaml:"spec,omitempty"`
	Status ManifestSyncStatus `yaml:"status,omitempty"`
}

ManifestSync continually syncs unhyrated manifests to a hydrated repo. As part of that images are replaced with the latest images.

func (*ManifestSync) IsValid

func (m *ManifestSync) IsValid() error

IsValid verifies this is a fully valid manifest

type ManifestSyncSpec

type ManifestSyncSpec struct {
	SourceRepo GitHubRepo `yaml:"sourceRepo,omitempty"`
	// ForkRepo is the repo into which the hydrated manifests will be pushed
	ForkRepo GitHubRepo `yaml:"forkRepo,omitempty"`
	// DestRepo is the repo into which a PR will be created to merge hydrated
	// manifests from the ForkRepo
	DestRepo GitHubRepo `yaml:"destRepo,omitempty"`

	// SourcePath is relative to root of SourceRepo. It is the director
	// to search for manifests to hydrate
	SourcePath string `yaml:"sourcePath,omitempty"`

	// Selector selects which kustomizations to be used by matching kustomize labels.
	Selector *LabelSelector `yaml:"selector,omitempty"`

	// Only kustomizations which include these annotations as commonAnnotations will be hydrated.
	// Deprecated; selector should be used instead.
	MatchAnnotations map[string]string `yaml:"matchAnnotations,omitempty"`

	// DestPath is the directory in the destination repo where hydrated manifests should be emitted.
	// This directory will be deleted and recreated for each PR to ensure pruned resources are removed
	DestPath string `yaml:"destPath,omitempty"`

	// ImageTagsToPin is a list of image tags whose images should be pinned.
	// It is a replacement for ImageTags.
	ImageTagsToPin []ImageTagToPin `yaml:"imageTagsToPin,omitempty"`

	// ImageRegistries is a list of image registries. Only images in these registries with one of
	// the ImageTags labeled above is eligible for replacement.
	ImageRegistries []string `yaml:"imageRegistries,omitempty"`

	// ImageBuilder configures the image building.
	ImageBuilder *ImageBuilder `yaml:"imageBuilder,omitempty"`

	// ExcludeDirs is a list of paths relative to the repo root exclude. This is typically directories that
	// store templates. These directories will not be considered at all; e.g.
	//  1. Manifests are not eligible for image replacement
	//  2. Manifests are not eligible for hydration
	// If you need to #1 but not #2 use MatchAnnotations to exclude a kustomization from hydration but not image
	// replacement.
	ExcludeDirs []string `yaml:"excludeDirs,omitempty"`

	// PrLabels is a list of labels to add to the PR.
	PrLabels []string `yaml:"prLabels,omitempty"`

	// Functions is a list of kustomize functions to apply to the hydrated manifests
	Functions []Function `yaml:"functions,omitempty"`
}

ManifestSyncSpec is the spec for ManifestSync.

type ManifestSyncStatus

type ManifestSyncStatus struct {
	// PausedUntil is a timestamp indicating when the sync should be paused until.
	PausedUntil  *metav1.Time  `yaml:"pausedUntil,omitempty"`
	SourceURL    string        `yaml:"sourceUrl,omitempty"`
	SourceCommit string        `yaml:"sourceCommit,omitempty"`
	PinnedImages []PinnedImage `yaml:"pinnedImages,omitempty"`
}

ManifestSyncStatus is the status for ManifestSync resources.

type Metadata

type Metadata struct {
	Name        string            `yaml:"name,omitempty"`
	Namespace   string            `yaml:"namespace,omitempty"`
	Labels      map[string]string `yaml:"labels"`
	Annotations map[string]string `yaml:"annotations,omitempty"`
	// ResourceVersion is used for optimistic concurrency.
	// Ref: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#metadata
	// This should be treated as an opaque value by clients.
	ResourceVersion string `yaml:"resourceVersion,omitempty"`
}

Metadata holds an optional name of the project.

type NonTerminalError

type NonTerminalError string

NonTerminalError error that we can safely ignore

func (NonTerminalError) Error

func (e NonTerminalError) Error() string

type PinnedImage

type PinnedImage struct {
	Image    string `yaml:"image,omitempty"`
	NewImage string `yaml:"newImage,omitempty"`
}

PinnedImage represents the mapping of an image to the value it should be pinned to.

type ReplicatedImage added in v0.0.7

type ReplicatedImage struct {
	APIVersion string              `yaml:"apiVersion" yamltags:"required"`
	Kind       string              `yaml:"kind" yamltags:"required"`
	Metadata   Metadata            `yaml:"metadata,omitempty"`
	Spec       ReplicatedImageSpec `yaml:"spec,omitempty"`
}

ReplicatedImage replicates an image to one or more locations. This is useful for using Artifact registry to build images and then copying them to GHCR.

type ReplicatedImageSource added in v0.0.7

type ReplicatedImageSource struct {
	// Repository is the full path of the image to replicate
	// e.g.us-west1-docker.pkg.dev/some-project/images/hydros
	// So it includes the registry and repository but not the tag or digest
	Repository string `yaml:"repository,omitempty"`
}

type ReplicatedImageSpec added in v0.0.7

type ReplicatedImageSpec struct {
	// Source is the source of the image to replicate
	Source ReplicatedImageSource `yaml:"source,omitempty"`
	// Destinations are the destination repositories to replicate the image to
	Destinations []string `yaml:"destinations,omitempty"`
}

type RepoConfig added in v0.0.3

type RepoConfig struct {
	APIVersion string   `yaml:"apiVersion"`
	Kind       string   `yaml:"kind"`
	Metadata   Metadata `yaml:"metadata"`
	Spec       RepoSpec `yaml:"spec"`
}

RepoConfig specifies a repository that should be checked out and periodically sync'd. TODO(jeremy): RepoConfig is a terrible name.

func (*RepoConfig) IsValid added in v0.0.3

func (c *RepoConfig) IsValid() (string, bool)

IsValid returns true if the config is valid. For invalid config the string will be a message of validation errors

type RepoMapping added in v0.0.5

type RepoMapping struct {
	// Input is the input URI of the repository to use.
	Input string `yaml:"input"`
	// Output is the output repostiroy to use.
	Output string `yaml:"output"`
}

RepoMapping is a mapping from a repository to a directory

type RepoMatchType

type RepoMatchType string

RepoMatchType is an enum for how repo matching rules should be applied.

type RepoSpec added in v0.0.3

type RepoSpec struct {
	// Repo is the URI of the repository to use.
	// You can specify a branch using the ref parameter specifies the reference to checkout
	// https://github.com/hashicorp/go-getter#protocol-specific-options
	Repo string `yaml:"repo"`

	// Globs is a list of globs to search for resources to sync.
	Globs []string `yaml:"globs,omitempty"`

	// Selectors is one or more labelselectors used to filter resources
	// to sync. A resource must match one of the label selectors in order to be included
	Selectors []LabelSelector `yaml:"selectors,omitempty"`

	// Pause causes the controller to pause regular ManifestSync hydration for the specified amount of type
	// This causes the manifests to be hydrated in a takeover configuration
	Pause string `yaml:"pause,omitempty"`

	// RepoMappings is a list of one or more mappings from one repository to another repository(or branch).
	// This is used to rewrite the sourceRepositories in ManifestSync resources in order to hydrate from a
	// branch.
	RepoMappings []RepoMapping `yaml:"repoMappings,omitempty"`
}

RepoSpec is the spec for a repository to synchronize

type S3AssetsList

type S3AssetsList struct {
	APIVersion string   `yaml:"apiVersion"`
	Kind       string   `yaml:"kind"`
	Metadata   Metadata `yaml:"metadata,omitempty"`

	S3Assets []string `yaml:"s3assets,omitempty"`
}

S3AssetsList is a list of s3 paths

type S3NonTerminalError

type S3NonTerminalError string

S3NonTerminalError a non terminal s3 error

const (
	// ErrS3IsADirectory a non terminal error for a directory that already exists
	ErrS3IsADirectory S3NonTerminalError = "is a directory"
	// ErrS3AssetAlreadyDownloaded a non terminal for an already downloaded s3 asset
	ErrS3AssetAlreadyDownloaded S3NonTerminalError = "Asset already downloaded"
)

func (S3NonTerminalError) Error

func (e S3NonTerminalError) Error() string

func (S3NonTerminalError) Unwrap

func (e S3NonTerminalError) Unwrap() error

Unwrap func to unwrap a wrapped err

type SourceMapping added in v0.0.3

type SourceMapping struct {
	// Src is a glob pattern to match local paths against. Directories should be delimited by / on all platforms.
	// e.g. "css/**/*.css"
	Src string `yaml:"src,omitempty"`
	// Dest is the path to copy the files to in the artifact.
	// e.g. "ghapp"
	Dest string `yaml:"dest,omitempty"`
	// Strip is the path prefix to strip from all paths
	Strip string `yaml:"strip,omitempty"`
}

SourceMapping specifies how source files are mapped into the destination artifact It is inspired by skaffold; https://skaffold.dev/docs/references/yaml/ When building images from a YAML file the src is a relative path to the location of the YAML file. src can start with a parent prefix e.g. ".." to obtain files higher in the directory tree relative to the YAML file. The parent directory wil then be used when computing the destination path. e.g. if we have /p/a/image.yaml /p/b/file.txt And image.yaml contains src "../b/file.txt" then the file will be copied to b/file.txt by default in the tarball

type Strategy

type Strategy string

Strategy is an enum for the strategy to use to tag images.

type TerminalError

type TerminalError string

TerminalError error that should cause a panic

Jump to

Keyboard shortcuts

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