From 1158d23299e52ceacc52e11327ca0a051648bbba Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Wed, 21 Apr 2021 19:11:06 +0300 Subject: [PATCH] Add API docs for v1alpha2 Signed-off-by: Stefan Prodan --- README.md | 3 + docs/spec/v1alpha2/imagepolicies.md | 221 ++++++++++++++++++++++++ docs/spec/v1alpha2/imagerepositories.md | 193 +++++++++++++++++++++ 3 files changed, 417 insertions(+) create mode 100644 docs/spec/v1alpha2/imagepolicies.md create mode 100644 docs/spec/v1alpha2/imagerepositories.md diff --git a/README.md b/README.md index fdfac4a5..9c0ef3fd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Image (metadata) reflector controller [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4790/badge)](https://bestpractices.coreinfrastructure.org/projects/4790) +[![report](https://goreportcard.com/badge/github.com/fluxcd/image-reflector-controller)](https://goreportcard.com/report/github.com/fluxcd/image-reflector-controller) +[![license](https://img.shields.io/github/license/fluxcd/image-reflector-controller.svg)](https://github.com/fluxcd/image-reflector-controller/blob/main/LICENSE) +[![release](https://img.shields.io/github/release/fluxcd/image-reflector-controller/all.svg)](https://github.com/fluxcd/image-reflector-controller/releases) This is a controller that reflects container image metadata into a Kubernetes cluster. It pairs with the [image update automation][auto] diff --git a/docs/spec/v1alpha2/imagepolicies.md b/docs/spec/v1alpha2/imagepolicies.md new file mode 100644 index 00000000..8c5d74f7 --- /dev/null +++ b/docs/spec/v1alpha2/imagepolicies.md @@ -0,0 +1,221 @@ + +# Image Policies + +The `ImagePolicy` type gives rules for selecting a "latest" image from a scanned +`ImageRepository`. This can be used to drive automation, as with the +[image-automation-controller][]; +or more generally, to inform other processes of the state of an +image repository. + +## Specification + +```go +// ImagePolicySpec defines the parameters for calculating the +// ImagePolicy +type ImagePolicySpec struct { + // ImageRepositoryRef points at the object specifying the image + // being scanned + // +required + ImageRepositoryRef corev1.LocalObjectReference `json:"imageRepositoryRef"` + // Policy gives the particulars of the policy to be followed in + // selecting the most recent image + // +required + Policy ImagePolicyChoice `json:"policy"` + // FilterTags enables filtering for only a subset of tags based on a set of + // rules. If no rules are provided, all the tags from the repository will be + // ordered and compared. + // +optional + FilterTags *TagFilter `json:"filterTags,omitempty"` +} +``` + +The field `ImageRepositoryRef` names an `ImageRepository` object in the same namespace. It is this +object that provides the scanned image metadata for the policy to use in selecting an image. + +### Policy + +The ImagePolicy field specifies how to choose a latest image given the image metadata. The choice is +between + +- **SemVer**: interpreting all tags as semver versions, and choosing the highest version available + that fits the given [semver constraints][semver-range]; or, +- **Alphabetical**: choosing the _last_ tag when all the tags are sorted alphabetically (in either + ascending or descending order); or, +- **Numerical**: choosing the _last_ tag when all the tags are sorted numerically (in either + ascending or descending order). + +```go +// ImagePolicyChoice is a union of all the types of policy that can be supplied. +type ImagePolicyChoice struct { + // SemVer gives a semantic version range to check against the tags available. + // +optional + SemVer *SemVerPolicy `json:"semver,omitempty"` + + // Alphabetical set of rules to use for alphabetical ordering of the tags. + // +optional + Alphabetical *AlphabeticalPolicy `json:"alphabetical,omitempty"` + + // Numerical set of rules to use for numerical ordering of the tags. + // +optional + Numerical *NumericalPolicy `json:"numerical,omitempty"` +} + +// SemVerPolicy specifies a semantic version policy. +type SemVerPolicy struct { + // Range gives a semver range for the image tag; the highest + // version within the range that's a tag yields the latest image. + // +required + Range string `json:"range"` +} + +// AlphabeticalPolicy specifies a alphabetical ordering policy. +type AlphabeticalPolicy struct { + // Order specifies the sorting order of the tags. Given the letters of the + // alphabet as tags, ascending order would select Z, and descending order + // would select A. + // +kubebuilder:default:="asc" + // +kubebuilder:validation:Enum=asc;desc + // +optional +Order string `json:"order,omitempty"` +} + +// NumericalPolicy specifies a numerical ordering policy. +type NumericalPolicy struct { + // Order specifies the sorting order of the tags. Given the integer values + // from 0 to 9 as tags, ascending order would select 9, and descending order + // would select 0. + // +kubebuilder:default:="asc" + // +kubebuilder:validation:Enum=asc;desc + // +optional + Order string `json:"order,omitempty"` +} +``` + +### FilterTags + +```go +// TagFilter enables filtering tags based on a set of defined rules +type TagFilter struct { + // Pattern specifies a regular expression pattern used to filter for image + // tags. + // +optional + Pattern string `json:"pattern"` + // Extract allows a capture group to be extracted from the specified regular + // expression pattern, useful before tag evaluation. + // +optional + Extract string `json:"extract"` +} +``` + +The `FilterTags` field gives you the opportunity to filter the image tags _before_ they are +considered by the policy rule. + +The `Pattern` field takes a [regular expression][regex-go] which can match anywhere in the tag string. +Only tags that match the pattern are considered by the policy rule. + +The optional `Extract` value will be expanded for each tag that matches the pattern. The resulting +values will be supplied to the policy rule instead of the original tags. If `Extract` is empty, then +the tags that match the pattern will be used as they are. + +## Status + +```go +// ImagePolicyStatus defines the observed state of ImagePolicy +type ImagePolicyStatus struct { + // LatestImage gives the first in the list of images scanned by + // the image repository, when filtered and ordered according to + // the policy. + LatestImage string `json:"latestImage,omitempty"` + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` +} +``` + +The `LatestImage` field contains the image selected by the policy rule, when it has run successfully. + +### Conditions + +There is one condition that may be present: the GitOps toolkit-standard `ReadyCondition`. This will +be marked as true when the policy rule has selected an image. + +## Examples + +Select the latest `main` branch build tagged as `${GIT_BRANCH}-${GIT_SHA:0:7}-$(date +%s)` (numerical): + +```yaml +kind: ImagePolicy +spec: + filterTags: + pattern: '^main-[a-fA-F0-9]+-(?P.*)' + extract: '$ts' + policy: + numerical: + order: asc +``` + +A more strict filter would be `^main-[a-fA-F0-9]+-(?P[1-9][0-9]*)`. +Before applying policies in-cluster, you can validate your filters using +a [Go regular expression tester](https://regoio.herokuapp.com) +or [regex101.com](https://regex101.com/). + +Select the latest stable version (semver): + +```yaml +kind: ImagePolicy +spec: + policy: + semver: + range: '>=1.0.0' +``` + +Select the latest stable patch version in the 1.x range (semver): + +```yaml +kind: ImagePolicy +spec: + policy: + semver: + range: '>=1.0.0 <2.0.0' +``` + +Select the latest version including pre-releases (semver): + +```yaml +kind: ImagePolicy +spec: + policy: + semver: + range: '>=1.0.0-0' +``` + +Select the latest release candidate (semver): + +```yaml +kind: ImagePolicy +spec: + filterTags: + pattern: '.*-rc.*' + policy: + semver: + range: '^1.x-0' +``` + +Select the latest release tagged as `RELEASE.` +e.g. [Minio](https://hub.docker.com/r/minio/minio) (alphabetical): + +```yaml +kind: ImagePolicy +spec: + filterTags: + pattern: '^RELEASE\.(?P.*)Z$' + extract: '$timestamp' + policy: + alphabetical: + order: asc +``` + +[image-automation-controller]: https://github.com/fluxcd/image-automation-controller +[semver-range]: https://github.com/Masterminds/semver#checking-version-constraints +[regex-go]: https://golang.org/pkg/regexp/syntax diff --git a/docs/spec/v1alpha2/imagerepositories.md b/docs/spec/v1alpha2/imagerepositories.md new file mode 100644 index 00000000..9a249df0 --- /dev/null +++ b/docs/spec/v1alpha2/imagerepositories.md @@ -0,0 +1,193 @@ + +# Image Repositories + +The `ImageRepository` API specifies how to scan OCI image repositories. A _repository_ is a +collection of images -- e.g., `alpine`, as opposed to a specific image, e.g., +`alpine:3.1`. `ImagePolicy` objects can then refer to an `ImageRepository` in order to select a +specific image from those scanned. + +## Specification + +The **ImageRepository** type holds details for scanning a particular image repository. + +```go +// ImageRepositorySpec defines the parameters for scanning an image +// repository, e.g., `fluxcd/flux`. +type ImageRepositorySpec struct { + // Image is the name of the image repository + // +required + Image string `json:"image,omitempty"` + // Interval is the length of time to wait between + // scans of the image repository. + // +required + Interval metav1.Duration `json:"interval,omitempty"` + + // Timeout for image scanning. + // Defaults to 'Interval' duration. + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // SecretRef can be given the name of a secret containing + // credentials to use for the image registry. The secret should be + // created with `kubectl create secret docker-registry`, or the + // equivalent. + // +optional + SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"` + + // CertSecretRef can be given the name of a secret containing + // either or both of + // + // - a PEM-encoded client certificate (`certFile`) and private + // key (`keyFile`); + // - a PEM-encoded CA certificate (`caFile`) + // + // and whichever are supplied, will be used for connecting to the + // registry. The client cert and key are useful if you are + // authenticating with a certificate; the CA cert is useful if + // you are using a self-signed server certificate. + // +optional + CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"` + + // This flag tells the controller to suspend subsequent image scans. + // It does not apply to already started scans. Defaults to false. + // +optional + Suspend bool `json:"suspend,omitempty"` +} +``` + +The `Suspend` field can be set to `true` to stop the controller scanning the image repository +specified; remove the field value or set to `false` to resume scanning. + +**`secretRef` for credentials** + +The `secretRef` names a secret in the same namespace that holds credentials for accessing the image +repository. This secret is expected to be in the same format as for +[`imagePullSecrets`][image-pull-secrets]. The usual way to create such a secret is with + + kubectl create secret docker-registry ... + +If you are running on a platform (e.g., AWS) that links service permissions (e.g., access to ECR) to +service accounts, you may need to create the secret using tooling for that platform instead. There +is advice specific to some platforms [in the image automation guide][image-auto-provider-secrets]. + +For a publicly accessible image repository, you don't need to provide a `secretRef`. + +**`certSecretRef` for TLS certificates** + +The `certSecretRef` field names a secret with TLS certificate data. This is for two separate +purposes: + + - to provide a client certificate and private key, if you use a certificate to authenticate with + the image registry; and, + - to provide a CA certificate, if the registry uses a self-signed certificate. + +These will often go together, if you are hosting an image registry yourself. All the files in the +secret are expected to be [PEM-encoded][pem-encoding]. This is an ASCII format for certificates and +keys; `openssl` and such tools will typically give you an option of PEM output. + +Assuming you have obtained a certificate file and private key and put them in the files `client.crt` +and `client.key` respectively, you can create a secret with `kubectl` like this: + +```bash +SECRET_NAME=tls-certs +kubectl create secret generic $SECRET_NAME \ + --from-file=certFile=client.crt \ + --from-file=keyFile=client.key +``` + +You could also [prepare a secret and encrypt it][sops-guide]; the important bit is that the data +keys in the secret are `certFile` and `keyFile`. + +If you have a CA certificate for the client to use, the data key for that is `caFile`. Adapting the +previous example, if you have the certificate in the file `ca.crt`, and the client certificate and +key as before, the whole command would be: + +```bash +SECRET_NAME=tls-certs +kubectl create secret generic $SECRET_NAME \ + --from-file=certFile=client.crt \ + --from-file=keyFile=client.key \ + --from-file=caFile=ca.crt +``` + +## Status + +```go +// ImageRepositoryStatus defines the observed state of ImageRepository +type ImageRepositoryStatus struct { + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // ObservedGeneration is the last reconciled generation. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // CannonicalName is the name of the image repository with all the + // implied bits made explicit; e.g., `docker.io/library/alpine` + // rather than `alpine`. + // +optional + CanonicalImageName string `json:"canonicalImageName,omitempty"` + + // LastScanResult contains the number of fetched tags. + // +optional + LastScanResult *ScanResult `json:"lastScanResult,omitempty"` + + meta.ReconcileRequestStatus `json:",inline"` +} +``` + +The `CanonicalName` field gives the fully expanded image name, filling in any parts left implicit in +the spec. For instance, `alpine` expands to `docker.io/library/alpine`. + +The `LastScanResult` field gives a summary of the most recent scan: + +```go +type ScanResult struct { + TagCount int `json:"tagCount"` + ScanTime metav1.Time `json:"scanTime,omitempty"` +} +``` + +### Conditions + +There is one condition used: the GitOps toolkit-standard `ReadyCondition`. This will be marked as +true when a scan succeeds, and false when a scan fails. + +### Examples + +Fetch metadata for a public image every ten minutes: + +```yaml +apiVersion: image.toolkit.fluxcd.io/v1alpha1 +kind: ImageRepository +metadata: + name: podinfo + namespace: flux-system +spec: + interval: 10m + image: ghcr.io/stefanprodan/podinfo +``` + +Fetch metadata for a private image every minute: + +```yaml +apiVersion: image.toolkit.fluxcd.io/v1alpha1 +kind: ImageRepository +metadata: + name: podinfo + namespace: flux-system +spec: + interval: 1m0s + image: docker.io/org/image + secretRef: + name: regcred +``` + +For private images, you can create a Kubernetes secret in the same namespace +as the `ImageRepository` with `kubectl create secret docker-registry`, +and reference it under `secretRef`. + +[image-pull-secrets]: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod +[image-auto-provider-secrets]: https://toolkit.fluxcd.io/guides/image-update/#imagerepository-cloud-providers-authentication +[pem-encoding]: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail +[sops-guide]: https://toolkit.fluxcd.io/guides/mozilla-sops/