docs: Add API spec for Helm v1
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
parent
f85bc174e6
commit
0a7f66b0e3
14
README.md
14
README.md
|
@ -16,13 +16,13 @@ and is a core component of the [GitOps toolkit](https://fluxcd.io/flux/component
|
|||
|
||||
## APIs
|
||||
|
||||
| Kind | API Version |
|
||||
|---------------------------------------------------------|------------------------------------|
|
||||
| [GitRepository](docs/spec/v1/gitrepositories.md) | `source.toolkit.fluxcd.io/v1` |
|
||||
| [OCIRepository](docs/spec/v1beta2/ocirepositories.md) | `source.toolkit.fluxcd.io/v1beta2` |
|
||||
| [HelmRepository](docs/spec/v1beta2/helmrepositories.md) | `source.toolkit.fluxcd.io/v1beta2` |
|
||||
| [HelmChart](docs/spec/v1beta2/helmcharts.md) | `source.toolkit.fluxcd.io/v1beta2` |
|
||||
| [Bucket](docs/spec/v1beta2/buckets.md) | `source.toolkit.fluxcd.io/v1beta2` |
|
||||
| Kind | API Version |
|
||||
|-------------------------------------------------------|------------------------------------|
|
||||
| [GitRepository](docs/spec/v1/gitrepositories.md) | `source.toolkit.fluxcd.io/v1` |
|
||||
| [OCIRepository](docs/spec/v1beta2/ocirepositories.md) | `source.toolkit.fluxcd.io/v1beta2` |
|
||||
| [HelmRepository](docs/spec/v1/helmrepositories.md) | `source.toolkit.fluxcd.io/v1` |
|
||||
| [HelmChart](docs/spec/v1/helmcharts.md) | `source.toolkit.fluxcd.io/v1` |
|
||||
| [Bucket](docs/spec/v1beta2/buckets.md) | `source.toolkit.fluxcd.io/v1beta2` |
|
||||
|
||||
## Features
|
||||
|
||||
|
|
|
@ -78,6 +78,11 @@ type HelmChartSpec struct {
|
|||
// +deprecated
|
||||
ValuesFile string `json:"valuesFile,omitempty"`
|
||||
|
||||
// IgnoreMissingValuesFiles controls whether to silently ignore missing values
|
||||
// files rather than failing.
|
||||
// +optional
|
||||
IgnoreMissingValuesFiles bool `json:"ignoreMissingValuesFiles,omitempty"`
|
||||
|
||||
// Suspend tells the controller to suspend the reconciliation of this
|
||||
// source.
|
||||
// +optional
|
||||
|
@ -141,6 +146,12 @@ type HelmChartStatus struct {
|
|||
// +optional
|
||||
ObservedChartName string `json:"observedChartName,omitempty"`
|
||||
|
||||
// ObservedValuesFiles are the observed value files of the last successful
|
||||
// reconciliation.
|
||||
// It matches the chart in the last successfully reconciled artifact.
|
||||
// +optional
|
||||
ObservedValuesFiles []string `json:"observedValuesFiles,omitempty"`
|
||||
|
||||
// Conditions holds the conditions for the HelmChart.
|
||||
// +optional
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
|
|
|
@ -360,6 +360,11 @@ func (in *HelmChartSpec) DeepCopy() *HelmChartSpec {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartStatus) DeepCopyInto(out *HelmChartStatus) {
|
||||
*out = *in
|
||||
if in.ObservedValuesFiles != nil {
|
||||
in, out := &in.ObservedValuesFiles, &out.ObservedValuesFiles
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]metav1.Condition, len(*in))
|
||||
|
|
|
@ -745,6 +745,11 @@ spec:
|
|||
Chart is the name or path the Helm chart is available at in the
|
||||
SourceRef.
|
||||
type: string
|
||||
ignoreMissingValuesFiles:
|
||||
description: |-
|
||||
IgnoreMissingValuesFiles controls whether to silently ignore missing values
|
||||
files rather than failing.
|
||||
type: boolean
|
||||
interval:
|
||||
description: |-
|
||||
Interval at which the HelmChart SourceRef is checked for updates.
|
||||
|
@ -1020,6 +1025,14 @@ spec:
|
|||
ObservedSourceArtifactRevision is the last observed Artifact.Revision
|
||||
of the HelmChartSpec.SourceRef.
|
||||
type: string
|
||||
observedValuesFiles:
|
||||
description: |-
|
||||
ObservedValuesFiles are the observed value files of the last successful
|
||||
reconciliation.
|
||||
It matches the chart in the last successfully reconciled artifact.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
url:
|
||||
description: |-
|
||||
URL is the dynamic fetch link for the latest Artifact.
|
||||
|
|
|
@ -408,6 +408,19 @@ is merged before the ValuesFiles items. Ignored when omitted.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>ignoreMissingValuesFiles</code><br>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>IgnoreMissingValuesFiles controls whether to silently ignore missing values
|
||||
files rather than failing.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>suspend</code><br>
|
||||
<em>
|
||||
bool
|
||||
|
@ -1492,6 +1505,19 @@ is merged before the ValuesFiles items. Ignored when omitted.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>ignoreMissingValuesFiles</code><br>
|
||||
<em>
|
||||
bool
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>IgnoreMissingValuesFiles controls whether to silently ignore missing values
|
||||
files rather than failing.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>suspend</code><br>
|
||||
<em>
|
||||
bool
|
||||
|
@ -1599,6 +1625,20 @@ resolved chart reference.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>observedValuesFiles</code><br>
|
||||
<em>
|
||||
[]string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ObservedValuesFiles are the observed value files of the last successful
|
||||
reconciliation.
|
||||
It matches the chart in the last successfully reconciled artifact.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>conditions</code><br>
|
||||
<em>
|
||||
<a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Condition">
|
||||
|
|
|
@ -6,6 +6,8 @@ This is the v1 API specification for defining the desired state sources of Kuber
|
|||
|
||||
* Source kinds:
|
||||
+ [GitRepository](gitrepositories.md)
|
||||
+ [HelmRepository](helmrepositories.md)
|
||||
+ [HelmChart](helmcharts.md)
|
||||
|
||||
## Implementation
|
||||
|
||||
|
|
|
@ -0,0 +1,865 @@
|
|||
# Helm Charts
|
||||
|
||||
<!-- menuweight:50 -->
|
||||
|
||||
The `HelmChart` API defines a Source to produce an Artifact for a Helm chart
|
||||
archive with a set of specific configurations.
|
||||
|
||||
## Example
|
||||
|
||||
The following is an example of a HelmChart. It fetches and/or packages a Helm
|
||||
chart and exposes it as a tarball (`.tgz`) Artifact for the specified
|
||||
configuration:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 5m0s
|
||||
chart: podinfo
|
||||
reconcileStrategy: ChartVersion
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
version: '5.*'
|
||||
```
|
||||
|
||||
In the above example:
|
||||
|
||||
- A HelmChart named `podinfo` is created, indicated by the `.metadata.name`
|
||||
field.
|
||||
- The source-controller fetches the Helm chart every five minutes from the
|
||||
`podinfo` HelmRepository source reference, indicated by the
|
||||
`.spec.sourceRef.kind` and `.spec.sourceRef.name` fields.
|
||||
- The fetched Helm chart version is the latest available chart
|
||||
version in the range specified in `spec.version`. This version is also used as
|
||||
Artifact revision, reported in-cluster in the `.status.artifact.revision`
|
||||
field.
|
||||
- When the current Helm Chart version differs from the latest available chart
|
||||
in the version range, it is fetched and/or packaged as a new Artifact.
|
||||
- The new Artifact is reported in the `.status.artifact` field.
|
||||
|
||||
You can run this example by saving the manifest into `helmchart.yaml`.
|
||||
|
||||
**Note:** HelmChart is usually used by the helm-controller. Based on the
|
||||
HelmRelease configuration, an associated HelmChart is created by the
|
||||
helm-controller.
|
||||
|
||||
1. Apply the resource on the cluster:
|
||||
|
||||
```sh
|
||||
kubectl apply -f helmchart.yaml
|
||||
```
|
||||
|
||||
2. Run `kubectl get helmchart` to see the HelmChart:
|
||||
|
||||
```console
|
||||
NAME CHART VERSION SOURCE KIND SOURCE NAME AGE READY STATUS
|
||||
podinfo podinfo 5.* HelmRepository podinfo 53s True pulled 'podinfo' chart with version '5.2.1'
|
||||
```
|
||||
|
||||
3. Run `kubectl describe helmchart podinfo` to see the [Artifact](#artifact) and
|
||||
[Conditions](#conditions) in the HelmChart's Status:
|
||||
|
||||
```console
|
||||
Status:
|
||||
Observed Source Artifact Revision: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
|
||||
Artifact:
|
||||
Digest: sha256:6c3cc3b955bce1686036ae6822ee2ca0ef6ecb994e3f2d19eaf3ec03dcba84b3
|
||||
Last Update Time: 2022-02-13T11:24:10Z
|
||||
Path: helmchart/default/podinfo/podinfo-5.2.1.tgz
|
||||
Revision: 5.2.1
|
||||
Size: 14166
|
||||
URL: http://source-controller.flux-system.svc.cluster.local./helmchart/default/podinfo/podinfo-5.2.1.tgz
|
||||
Conditions:
|
||||
Last Transition Time: 2022-02-13T11:24:10Z
|
||||
Message: pulled 'podinfo' chart with version '5.2.1'
|
||||
Observed Generation: 1
|
||||
Reason: ChartPullSucceeded
|
||||
Status: True
|
||||
Type: Ready
|
||||
Last Transition Time: 2022-02-13T11:24:10Z
|
||||
Message: pulled 'podinfo' chart with version '5.2.1'
|
||||
Observed Generation: 1
|
||||
Reason: ChartPullSucceeded
|
||||
Status: True
|
||||
Type: ArtifactInStorage
|
||||
Observed Chart Name: podinfo
|
||||
Observed Generation: 1
|
||||
URL: http://source-controller.flux-system.svc.cluster.local./helmchart/default/podinfo/latest.tar.gz
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Normal ChartPullSucceeded 2m51s source-controller pulled 'podinfo' chart with version '5.2.1'
|
||||
```
|
||||
|
||||
## Writing a HelmChart spec
|
||||
|
||||
As with all other Kubernetes config, a HelmChart needs `apiVersion`, `kind`, and
|
||||
`metadata` fields. The name of a HelmChart object must be a valid
|
||||
[DNS subdomain name](https://kubernetes.io/docs/concepts/overview/working-with-objects/names#dns-subdomain-names).
|
||||
|
||||
A HelmChart also needs a
|
||||
[`.spec` section](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status).
|
||||
|
||||
### Source reference
|
||||
|
||||
`.spec.sourceRef` is a required field that specifies a reference to the Source
|
||||
the chart is available at.
|
||||
|
||||
Supported references are:
|
||||
- [`HelmRepository`](helmrepositories.md)
|
||||
- [`GitRepository`](gitrepositories.md)
|
||||
- [`Bucket`](buckets.md)
|
||||
|
||||
Although there are three kinds of source references, there are only two
|
||||
underlying implementations. The artifact building process for `GitRepository`
|
||||
and `Bucket` are the same as they are already built source artifacts. In case
|
||||
of `HelmRepository`, a chart is fetched and/or packaged based on the
|
||||
configuration of the Helm chart.
|
||||
|
||||
For a `HelmChart` to be reconciled, the associated artifact in the source
|
||||
reference must be ready. If the source artifact is not ready, the `HelmChart`
|
||||
reconciliation is retried.
|
||||
|
||||
When the `metadata.generation` of the `HelmChart` don't match with the
|
||||
`status.observedGeneration`, the chart is fetched from source and/or packaged.
|
||||
If there's no `.spec.valuesFiles` specified, the chart is only fetched from the
|
||||
source, and not packaged. If `.spec.valuesFiles` are specified, the chart is
|
||||
fetched and packaged with the values files. When the `metadata.generation`
|
||||
matches the `status.observedGeneration`, the chart is only fetched from source
|
||||
or from the cache if available, and not packaged.
|
||||
|
||||
When using a `HelmRepository` source reference, the secret reference defined in
|
||||
the Helm repository is used to fetch the chart.
|
||||
|
||||
The HelmChart reconciliation behavior varies depending on the source reference
|
||||
kind, see [reconcile strategy](#reconcile-strategy).
|
||||
|
||||
The attributes of the generated artifact also varies depending on the source
|
||||
reference kind, see [artifact](#artifact).
|
||||
|
||||
### Chart
|
||||
|
||||
`.spec.chart` is a required field that specifies the name or path the Helm chart
|
||||
is available at in the [Source reference](#source-reference).
|
||||
|
||||
For `HelmRepository` Source reference, it'll be just the name of the chart.
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
chart: podinfo
|
||||
sourceRef:
|
||||
name: podinfo
|
||||
kind: HelmRepository
|
||||
```
|
||||
|
||||
For `GitRepository` and `Bucket` Source reference, it'll be the path to the
|
||||
Helm chart directory.
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
chart: ./charts/podinfo
|
||||
sourceRef:
|
||||
name: podinfo
|
||||
kind: <GitRepository|Bucket>
|
||||
```
|
||||
|
||||
### Version
|
||||
|
||||
`.spec.version` is an optional field to specify the version of the chart in
|
||||
semver. It is applicable only when the Source reference is a `HelmRepository`.
|
||||
It is ignored for `GitRepository` and `Bucket` Source reference. It defaults to
|
||||
the latest version of the chart with value `*`.
|
||||
|
||||
Version can be a fixed semver, minor or patch semver range of a specific
|
||||
version (i.e. `4.0.x`) or any semver range (i.e. `>=4.0.0 <5.0.0`).
|
||||
|
||||
### Values files
|
||||
|
||||
`.spec.valuesFiles` is an optional field to specify an alternative list of
|
||||
values files to use as the chart values (values.yaml). The file paths are
|
||||
expected to be relative to the Source reference. Values files are merged in the
|
||||
order of the list with the last file overriding the first. It is ignored when
|
||||
omitted. When values files are specified, the chart is fetched and packaged
|
||||
with the provided values.
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: podinfo
|
||||
...
|
||||
valuesFiles:
|
||||
- values.yaml
|
||||
- values-production.yaml
|
||||
```
|
||||
|
||||
Values files also affect the generated artifact revision, see
|
||||
[artifact](#artifact).
|
||||
|
||||
### Ignore missing values files
|
||||
|
||||
`.spec.ignoreMissingValuesFiles` is an optional field to specify whether missing
|
||||
values files should be ignored rather than be considered errors. It defaults to
|
||||
`false`.
|
||||
|
||||
When `.spec.valuesFiles` and `.spec.ignoreMissingValuesFiles` are specified,
|
||||
the `.status.observedValuesFiles` field is populated with the list of values
|
||||
files that were found and actually contributed to the packaged chart.
|
||||
|
||||
### Reconcile strategy
|
||||
|
||||
`.spec.reconcileStrategy` is an optional field to specify what enables the
|
||||
creation of a new Artifact. Valid values are `ChartVersion` and `Revision`.
|
||||
`ChartVersion` is used for creating a new artifact when the chart version
|
||||
changes in a `HelmRepository`. `Revision` is used for creating a new artifact
|
||||
when the source revision changes in a `GitRepository` or a `Bucket` Source. It
|
||||
defaults to `ChartVersion`.
|
||||
|
||||
**Note:** If the reconcile strategy is `ChartVersion` and the source reference
|
||||
is a `GitRepository` or a `Bucket`, no new chart artifact is produced on updates
|
||||
to the source unless the `version` in `Chart.yaml` is incremented. To produce
|
||||
new chart artifact on change in source revision, set the reconcile strategy to
|
||||
`Revision`.
|
||||
|
||||
Reconcile strategy also affects the artifact version, see [artifact](#artifact)
|
||||
for more details.
|
||||
|
||||
### Interval
|
||||
|
||||
`.spec.interval` is a required field that specifies the interval at which the
|
||||
Helm Chart source must be checked for updates.
|
||||
|
||||
After successfully reconciling a HelmChart object, the source-controller
|
||||
requeues the object for inspection after the specified interval. The value must
|
||||
be in a [Go recognized duration string format](https://pkg.go.dev/time#ParseDuration),
|
||||
e.g. `10m0s` to look at the source for updates every 10 minutes.
|
||||
|
||||
If the `.metadata.generation` of a resource changes (due to e.g. applying a
|
||||
change to the spec), this is handled instantly outside the interval window.
|
||||
|
||||
**Note:** The controller can be configured to apply a jitter to the interval in
|
||||
order to distribute the load more evenly when multiple HelmChart objects are set
|
||||
up with the same interval. For more information, please refer to the
|
||||
[source-controller configuration options](https://fluxcd.io/flux/components/source/options/).
|
||||
|
||||
### Suspend
|
||||
|
||||
`.spec.suspend` is an optional field to suspend the reconciliation of a
|
||||
HelmChart. When set to `true`, the controller will stop reconciling the
|
||||
HelmChart, and changes to the resource or the Helm chart Source will not result
|
||||
in a new Artifact. When the field is set to `false` or removed, it will resume.
|
||||
|
||||
For practical information, see
|
||||
[suspending and resuming](#suspending-and-resuming).
|
||||
|
||||
### Verification
|
||||
|
||||
**Note:** This feature is available only for Helm charts fetched from an OCI Registry.
|
||||
|
||||
`.spec.verify` is an optional field to enable the verification of [Cosign](https://github.com/sigstore/cosign) or [Notation](https://github.com/notaryproject/notation)
|
||||
signatures. The field offers three subfields:
|
||||
|
||||
- `.provider`, to specify the verification provider. The supported options are `cosign` and `notation` at present.
|
||||
- `.secretRef.name`, to specify a reference to a Secret in the same namespace as
|
||||
the HelmChart, containing the public keys of trusted authors. For Notation this Secret should also include the [trust policy](https://github.com/notaryproject/specifications/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-policy) in
|
||||
addition to the CA certificate.
|
||||
- `.matchOIDCIdentity`, to specify a list of OIDC identity matchers (only supported when using `cosign` as the verification provider). Please see
|
||||
[Keyless verification](#keyless-verification) for more details.
|
||||
|
||||
#### Cosign
|
||||
|
||||
The `cosign` provider can be used to verify the signature of an OCI artifact using either a known public key or via the [Cosign Keyless](https://github.com/sigstore/cosign/blob/main/KEYLESS.md) procedure.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: podinfo
|
||||
spec:
|
||||
verify:
|
||||
provider: cosign
|
||||
secretRef:
|
||||
name: cosign-public-keys
|
||||
```
|
||||
|
||||
When the verification succeeds, the controller adds a Condition with the
|
||||
following attributes to the HelmChart's `.status.conditions`:
|
||||
|
||||
- `type: SourceVerified`
|
||||
- `status: "True"`
|
||||
- `reason: Succeeded`
|
||||
|
||||
##### Public keys verification
|
||||
|
||||
To verify the authenticity of HelmChart hosted in an OCI Registry, create a Kubernetes
|
||||
secret with the Cosign public keys:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cosign-public-keys
|
||||
type: Opaque
|
||||
data:
|
||||
key1.pub: <BASE64>
|
||||
key2.pub: <BASE64>
|
||||
```
|
||||
|
||||
Note that the keys must have the `.pub` extension for Flux to make use of them.
|
||||
|
||||
Flux will loop over the public keys and use them to verify a HelmChart's signature.
|
||||
This allows for older HelmCharts to be valid as long as the right key is in the secret.
|
||||
|
||||
##### Keyless verification
|
||||
|
||||
For publicly available HelmCharts, which are signed using the
|
||||
[Cosign Keyless](https://github.com/sigstore/cosign/blob/main/KEYLESS.md) procedure,
|
||||
you can enable the verification by omitting the `.verify.secretRef` field.
|
||||
|
||||
To verify the identity's subject and the OIDC issuer present in the Fulcio
|
||||
certificate, you can specify a list of OIDC identity matchers using
|
||||
`.spec.verify.matchOIDCIdentity`. The matcher provides two required fields:
|
||||
|
||||
- `.issuer`, to specify a regexp that matches against the OIDC issuer.
|
||||
- `.subject`, to specify a regexp that matches against the subject identity in
|
||||
the certificate.
|
||||
Both values should follow the [Go regular expression syntax](https://golang.org/s/re2syntax).
|
||||
|
||||
The matchers are evaluated in an OR fashion, i.e. the identity is deemed to be
|
||||
verified if any one matcher successfully matches against the identity.
|
||||
|
||||
Example of verifying HelmCharts signed by the
|
||||
[Cosign GitHub Action](https://github.com/sigstore/cosign-installer) with GitHub OIDC Token:
|
||||
|
||||
```yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: podinfo
|
||||
spec:
|
||||
interval: 5m
|
||||
chart: podinfo
|
||||
reconcileStrategy: ChartVersion
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: podinfo
|
||||
version: ">=6.1.6"
|
||||
verify:
|
||||
provider: cosign
|
||||
matchOIDCIdentity:
|
||||
- issuer: "^https://token.actions.githubusercontent.com$"
|
||||
subject: "^https://github.com/stefanprodan/podinfo.*$"
|
||||
```
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
spec:
|
||||
interval: 1m0s
|
||||
url: oci://ghcr.io/stefanprodan/charts
|
||||
type: "oci"
|
||||
```
|
||||
|
||||
The controller verifies the signatures using the Fulcio root CA and the Rekor
|
||||
instance hosted at [rekor.sigstore.dev](https://rekor.sigstore.dev/).
|
||||
|
||||
Note that keyless verification is an **experimental feature**, using
|
||||
custom root CAs or self-hosted Rekor instances are not currently supported.
|
||||
|
||||
#### Notation
|
||||
|
||||
The `notation` provider can be used to verify the signature of an OCI artifact using known
|
||||
trust policy and CA certificate.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: podinfo
|
||||
spec:
|
||||
verify:
|
||||
provider: notation
|
||||
secretRef:
|
||||
name: notation-config
|
||||
```
|
||||
|
||||
When the verification succeeds, the controller adds a Condition with the
|
||||
following attributes to the HelmChart's `.status.conditions`:
|
||||
|
||||
- `type: SourceVerified`
|
||||
- `status: "True"`
|
||||
- `reason: Succeeded`
|
||||
|
||||
To verify the authenticity of an OCI artifact, create a Kubernetes secret
|
||||
containing Certificate Authority (CA) root certificates and the a `trust policy`
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: notation-config
|
||||
type: Opaque
|
||||
data:
|
||||
certificate1.pem: <BASE64>
|
||||
certificate2.crt: <BASE64>
|
||||
trustpolicy.json: <BASE64>
|
||||
```
|
||||
|
||||
Note that the CA certificates must have either `.pem` or `.crt` extension and your trust policy must
|
||||
be named `trustpolicy.json` for Flux to make use of them.
|
||||
|
||||
For more information on the signing and verification process see [Signing and Verification Workflow](https://github.com/notaryproject/specifications/blob/v1.0.0/specs/signing-and-verification-workflow.md).
|
||||
|
||||
Flux will loop over the certificates and use them to verify an artifact's signature.
|
||||
This allows for older artifacts to be valid as long as the right certificate is in the secret.
|
||||
|
||||
## Working with HelmCharts
|
||||
|
||||
### Triggering a reconcile
|
||||
|
||||
To manually tell the source-controller to reconcile a HelmChart outside the
|
||||
[specified interval window](#interval), a HelmCHart can be annotated with
|
||||
`reconcile.fluxcd.io/requestedAt: <arbitrary value>`. Annotating the resource
|
||||
queues the object for reconciliation if the `<arbitrary-value>` differs from
|
||||
the last value the controller acted on, as reported in
|
||||
[`.status.lastHandledReconcileAt`](#last-handled-reconcile-at).
|
||||
|
||||
Using `kubectl`:
|
||||
|
||||
```sh
|
||||
kubectl annotate --field-manager=flux-client-side-apply --overwrite helmchart/<chart-name> reconcile.fluxcd.io/requestedAt="$(date +%s)"
|
||||
```
|
||||
|
||||
### Waiting for `Ready`
|
||||
|
||||
When a change is applied, it is possible to wait for the HelmChart to reach a
|
||||
[ready state](#ready-helmchart) using `kubectl`:
|
||||
|
||||
```sh
|
||||
kubectl wait helmchart/<chart-name> --for=condition=ready --timeout=1m
|
||||
```
|
||||
|
||||
### Suspending and resuming
|
||||
|
||||
When you find yourself in a situation where you temporarily want to pause the
|
||||
reconciliation of a HelmChart, you can suspend it using the
|
||||
[`.spec.suspend` field](#suspend).
|
||||
|
||||
#### Suspend a HelmChart
|
||||
|
||||
In your YAML declaration:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: <chart-name>
|
||||
spec:
|
||||
suspend: true
|
||||
```
|
||||
|
||||
Using `kubectl`:
|
||||
|
||||
```sh
|
||||
kubectl patch helmchart <chart-name> --field-manager=flux-client-side-apply -p '{\"spec\": {\"suspend\" : true }}'
|
||||
```
|
||||
|
||||
**Note:** When a HelmChart has an Artifact and is suspended, and this
|
||||
Artifact later disappears from the storage due to e.g. the source-controller
|
||||
Pod being evicted from a Node, this will not be reflected in the
|
||||
HelmChart's Status until it is resumed.
|
||||
|
||||
#### Resume a HelmChart
|
||||
|
||||
In your YAML declaration, comment out (or remove) the field:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: <chart-name>
|
||||
spec:
|
||||
# suspend: true
|
||||
```
|
||||
|
||||
**Note:** Setting the field value to `false` has the same effect as removing
|
||||
it, but does not allow for "hot patching" using e.g. `kubectl` while practicing
|
||||
GitOps; as the manually applied patch would be overwritten by the declared
|
||||
state in Git.
|
||||
|
||||
Using `kubectl`:
|
||||
|
||||
```sh
|
||||
kubectl patch helmchart <chart-name> --field-manager=flux-client-side-apply -p '{\"spec\" : {\"suspend\" : false }}'
|
||||
```
|
||||
|
||||
### Debugging a HelmChart
|
||||
|
||||
There are several ways to gather information about a HelmChart for debugging
|
||||
purposes.
|
||||
|
||||
#### Describe the HelmChart
|
||||
|
||||
Describing a HelmChart using `kubectl describe helmchart <chart-name>` displays
|
||||
the latest recorded information for the resource in the `Status` and `Events`
|
||||
sections:
|
||||
|
||||
```console
|
||||
...
|
||||
Status:
|
||||
...
|
||||
Conditions:
|
||||
Last Transition Time: 2022-02-13T14:06:27Z
|
||||
Message: invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with version matching '9.*' found
|
||||
Observed Generation: 3
|
||||
Reason: InvalidChartReference
|
||||
Status: True
|
||||
Type: Stalled
|
||||
Last Transition Time: 2022-02-13T14:06:27Z
|
||||
Message: invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with version matching '9.*' found
|
||||
Observed Generation: 3
|
||||
Reason: InvalidChartReference
|
||||
Status: False
|
||||
Type: Ready
|
||||
Last Transition Time: 2022-02-13T14:06:27Z
|
||||
Message: invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with version matching '9.*' found
|
||||
Observed Generation: 3
|
||||
Reason: InvalidChartReference
|
||||
Status: True
|
||||
Type: FetchFailed
|
||||
Last Handled Reconcile At: 1644759954
|
||||
Observed Chart Name: podinfo
|
||||
Observed Generation: 3
|
||||
URL: http://source-controller.flux-system.svc.cluster.local./helmchart/default/podinfo/latest.tar.gz
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Warning InvalidChartReference 11s source-controller invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with ver
|
||||
sion matching '9.*' found
|
||||
```
|
||||
|
||||
#### Trace emitted Events
|
||||
|
||||
To view events for specific HelmChart(s), `kubectl events` can be used in
|
||||
combination with `--for` to list the Events for specific objects. For example,
|
||||
running
|
||||
|
||||
```sh
|
||||
kubectl events --for HelmChart/<chart-name>
|
||||
```
|
||||
|
||||
lists
|
||||
|
||||
```console
|
||||
LAST SEEN TYPE REASON OBJECT MESSAGE
|
||||
22s Warning InvalidChartReference helmchart/<chart-name> invalid chart reference: failed to get chart version for remote reference: no 'podinfo' chart with version matching '9.*' found
|
||||
2s Normal ChartPullSucceeded helmchart/<chart-name> pulled 'podinfo' chart with version '6.0.3'
|
||||
2s Normal ArtifactUpToDate helmchart/<chart-name> artifact up-to-date with remote revision: '6.0.3'
|
||||
```
|
||||
|
||||
Besides being reported in Events, the reconciliation errors are also logged by
|
||||
the controller. The Flux CLI offer commands for filtering the logs for a
|
||||
specific HelmChart, e.g. `flux logs --level=error --kind=HelmChart --name=<chart-name>`.
|
||||
|
||||
### Improving resource consumption by enabling the cache
|
||||
|
||||
When using a `HelmRepository` as Source for a `HelmChart`, the controller loads
|
||||
the repository index in memory to find the latest version of the chart.
|
||||
|
||||
The controller can be configured to cache Helm repository indexes in memory.
|
||||
The cache is used to avoid loading repository indexes for every `HelmChart`
|
||||
reconciliation.
|
||||
|
||||
The following flags are provided to enable and configure the cache:
|
||||
- `helm-cache-max-size`: The maximum size of the cache in number of indexes.
|
||||
If `0`, then the cache is disabled.
|
||||
- `helm-cache-ttl`: The TTL of an index in the cache.
|
||||
- `helm-cache-purge-interval`: The interval at which the cache is purged of
|
||||
expired items.
|
||||
|
||||
The caching strategy is to pull a repository index from the cache if it is
|
||||
available, otherwise to load the index, retrieve and build the chart,
|
||||
then cache the index. The cached index TTL is refreshed every time the
|
||||
Helm repository index is loaded with the `helm-cache-ttl` value.
|
||||
|
||||
The cache is purged of expired items every `helm-cache-purge-interval`.
|
||||
|
||||
When the cache is full, no more items can be added to the cache, and the
|
||||
source-controller will report a warning event instead.
|
||||
|
||||
In order to use the cache, set the related flags in the source-controller
|
||||
Deployment config:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --watch-all-namespaces
|
||||
- --log-level=info
|
||||
- --log-encoding=json
|
||||
- --enable-leader-election
|
||||
- --storage-path=/data
|
||||
- --storage-adv-addr=source-controller.$(RUNTIME_NAMESPACE).svc.cluster.local.
|
||||
## Helm cache with up to 10 items, i.e. 10 indexes.
|
||||
- --helm-cache-max-size=10
|
||||
## TTL of an index is 1 hour.
|
||||
- --helm-cache-ttl=1h
|
||||
## Purge expired index every 10 minutes.
|
||||
- --helm-cache-purge-interval=10m
|
||||
```
|
||||
|
||||
## HelmChart Status
|
||||
|
||||
### Artifact
|
||||
|
||||
The HelmChart reports the last built chart as an Artifact object in the
|
||||
`.status.artifact` of the resource.
|
||||
|
||||
The Artifact file is a gzip compressed TAR archive (`<chart-name>-<chart-version>.tgz`),
|
||||
and can be retrieved in-cluster from the `.status.artifact.url` HTTP address.
|
||||
|
||||
#### Artifact example
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: <chart-name>
|
||||
status:
|
||||
artifact:
|
||||
digest: sha256:e30b95a08787de69ffdad3c232d65cfb131b5b50c6fd44295f48a078fceaa44e
|
||||
lastUpdateTime: "2022-02-10T18:53:47Z"
|
||||
path: helmchart/<source-namespace>/<chart-name>/<chart-name>-<chart-version>.tgz
|
||||
revision: 6.0.3
|
||||
size: 14166
|
||||
url: http://source-controller.flux-system.svc.cluster.local./helmchart/<source-namespace>/<chart-name>/<chart-name>-<chart-version>.tgz
|
||||
```
|
||||
|
||||
When using a `HelmRepository` as the source reference and values files are
|
||||
provided, the value of `status.artifact.revision` is the chart version combined
|
||||
with the `HelmChart` object generation. For example, if the chart version is
|
||||
`6.0.3` and the `HelmChart` object generation is `1`, the
|
||||
`status.artifact.revision` value will be `6.0.3+1`.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: <chart-name>
|
||||
status:
|
||||
artifact:
|
||||
digest: sha256:ee68224ded207ebb18a8e9730cf3313fa6bc1f31e6d8d3943ab541113559bb52
|
||||
lastUpdateTime: "2022-02-28T08:07:12Z"
|
||||
path: helmchart/<source-namespace>/<chart-name>/<chart-name>-6.0.3+1.tgz
|
||||
revision: 6.0.3+1
|
||||
size: 14166
|
||||
url: http://source-controller.flux-system.svc.cluster.local./helmchart/<source-namespace>/<chart-name>/<chart-name>-6.0.3+1.tgz
|
||||
observedGeneration: 1
|
||||
...
|
||||
```
|
||||
|
||||
When using a `GitRepository` or a `Bucket` as the source reference and
|
||||
`Revision` as the reconcile strategy, the value of `status.artifact.revision` is
|
||||
the chart version combined with the first 12 characters of the revision of the
|
||||
`GitRepository` or `Bucket`. For example if the chart version is `6.0.3` and the
|
||||
revision of the `Bucket` is `4e5cbb7b97d00a8039b8810b90b922f4256fd3bd8f78b934b4892dae13f7ca87`,
|
||||
the `status.artifact.revision` value will be `6.0.3+4e5cbb7b97d0`.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: <chart-name>
|
||||
status:
|
||||
artifact:
|
||||
digest: sha256:8d1f0ac3f4b0e8759a32180086f17ac87ca04e5d46c356e67f97e97616ef4718
|
||||
lastUpdateTime: "2022-02-28T08:07:12Z"
|
||||
path: helmchart/<source-namespace>/<chart-name>/<chart-name>-6.0.3+4e5cbb7b97d0.tgz
|
||||
revision: 6.0.3+4e5cbb7b97d0
|
||||
size: 14166
|
||||
url: http://source-controller.flux-system.svc.cluster.local./helmchart/<source-namespace>/<chart-name>/<chart-name>-6.0.3+4e5cbb7b97d0.tgz
|
||||
```
|
||||
|
||||
### Conditions
|
||||
|
||||
A HelmChart enters various states during its lifecycle, reflected as [Kubernetes
|
||||
Conditions][typical-status-properties].
|
||||
It can be [reconciling](#reconciling-helmchart) while fetching or building the
|
||||
chart, it can be [ready](#ready-helmchart), it can
|
||||
[fail during reconciliation](#failed-helmchart), or it can
|
||||
[stall](#stalled-helmchart).
|
||||
|
||||
The HelmChart API is compatible with the [kstatus
|
||||
specification][kstatus-spec],
|
||||
and reports `Reconciling` and `Stalled` conditions where applicable to
|
||||
provide better (timeout) support to solutions polling the HelmChart to become
|
||||
`Ready`.
|
||||
|
||||
#### Reconciling HelmChart
|
||||
|
||||
The source-controller marks a HelmChart as _reconciling_ when one of the
|
||||
following is true:
|
||||
|
||||
- There is no current Artifact for the HelmChart, or the reported Artifact is
|
||||
determined to have disappeared from the storage.
|
||||
- The generation of the HelmChart is newer than the [Observed
|
||||
Generation](#observed-generation).
|
||||
- The newly fetched Artifact revision differs from the current Artifact.
|
||||
|
||||
When the HelmChart is "reconciling", the `Ready` Condition status becomes
|
||||
`Unknown` when the controller detects drift, and the controller adds a Condition
|
||||
with the following attributes to the HelmChart's `.status.conditions`:
|
||||
|
||||
- `type: Reconciling`
|
||||
- `status: "True"`
|
||||
- `reason: Progressing` | `reason: ProgressingWithRetry`
|
||||
|
||||
If the reconciling state is due to a new version, it adds an additional
|
||||
Condition with the following attributes:
|
||||
|
||||
- `type: ArtifactOutdated`
|
||||
- `status: "True"`
|
||||
- `reason: NewChart`
|
||||
|
||||
Both Conditions have a ["negative polarity"][typical-status-properties],
|
||||
and are only present on the HelmChart while their status value is `"True"`.
|
||||
|
||||
#### Ready HelmChart
|
||||
|
||||
The source-controller marks a HelmChart as _ready_ when it has the following
|
||||
characteristics:
|
||||
|
||||
- The HelmChart reports an [Artifact](#artifact).
|
||||
- The reported Artifact exists in the controller's Artifact storage.
|
||||
- The controller was able to fetch and build the Helm chart using the current
|
||||
spec.
|
||||
- The version/revision of the reported Artifact is up-to-date with the
|
||||
latest version/revision of the Helm chart.
|
||||
|
||||
When the HelmChart is "ready", the controller sets a Condition with the
|
||||
following attributes in the HelmChart's `.status.conditions`:
|
||||
|
||||
- `type: Ready`
|
||||
- `status: "True"`
|
||||
- `reason: Succeeded`
|
||||
|
||||
This `Ready` Condition will retain a status value of `"True"` until the
|
||||
HelmChart is marked as [reconciling](#reconciling-helmchart), or e.g.
|
||||
a [transient error](#failed-helmchart) occurs due to a temporary network issue.
|
||||
|
||||
When the HelmChart Artifact is archived in the controller's Artifact
|
||||
storage, the controller sets a Condition with the following attributes in the
|
||||
HelmChart's `.status.conditions`:
|
||||
|
||||
- `type: ArtifactInStorage`
|
||||
- `status: "True"`
|
||||
- `reason: Succeeded`
|
||||
|
||||
This `ArtifactInStorage` Condition will retain a status value of `"True"` until
|
||||
the Artifact in the storage no longer exists.
|
||||
|
||||
#### Failed HelmChart
|
||||
|
||||
The source-controller may get stuck trying to produce an Artifact for a
|
||||
HelmChart without completing. This can occur due to some of the following
|
||||
factors:
|
||||
|
||||
- The Helm chart Source is temporarily unavailable.
|
||||
- The credentials in the [Source reference](#source-reference) Secret are
|
||||
invalid.
|
||||
- The HelmChart spec contains a generic misconfiguration.
|
||||
- A storage related failure when storing the artifact.
|
||||
|
||||
When this happens, the controller sets the `Ready` Condition status to `False`,
|
||||
and adds a Condition with the following attributes to the HelmChart's
|
||||
`.status.conditions`:
|
||||
|
||||
- `type: FetchFailed` | `type: StorageOperationFailed`
|
||||
- `status: "True"`
|
||||
- `reason: AuthenticationFailed` | `reason: StorageOperationFailed` | `reason: URLInvalid` | `reason: IllegalPath` | `reason: Failed`
|
||||
|
||||
This condition has a ["negative polarity"][typical-status-properties],
|
||||
and is only present on the HelmChart while the status value is `"True"`.
|
||||
There may be more arbitrary values for the `reason` field to provide accurate
|
||||
reason for a condition.
|
||||
|
||||
While the HelmChart has this Condition, the controller will continue to
|
||||
attempt to produce an Artifact for the resource with an exponential backoff,
|
||||
until it succeeds and the HelmChart is marked as [ready](#ready-helmchart).
|
||||
|
||||
Note that a HelmChart can be [reconciling](#reconciling-helmchart)
|
||||
while failing at the same time, for example due to a newly introduced
|
||||
configuration issue in the HelmChart spec. When a reconciliation fails, the
|
||||
`Reconciling` Condition reason would be `ProgressingWithRetry`. When the
|
||||
reconciliation is performed again after the failure, the reason is updated to
|
||||
`Progressing`.
|
||||
|
||||
#### Stalled HelmChart
|
||||
|
||||
The source-controller can mark a HelmChart as _stalled_ when it determines that
|
||||
without changes to the spec, the reconciliation can not succeed.
|
||||
For example because a HelmChart Version is set to a non-existing version.
|
||||
|
||||
When this happens, the controller sets the same Conditions as when it
|
||||
[fails](#failed-helmchart), but adds another Condition with the following
|
||||
attributes to the HelmChart's `.status.conditions`:
|
||||
|
||||
- `type: Stalled`
|
||||
- `status: "True"`
|
||||
- `reason: InvalidChartReference`
|
||||
|
||||
While the HelmChart has this Condition, the controller will not requeue the
|
||||
resource any further, and will stop reconciling the resource until a change to
|
||||
the spec is made.
|
||||
|
||||
### Observed Source Artifact Revision
|
||||
|
||||
The source-controller reports the revision of the last
|
||||
[Source reference's](#source-reference) Artifact the current chart was fetched
|
||||
from in the HelmChart's `.status.observedSourceArtifactRevision`. It is used to
|
||||
keep track of the source artifact revision and detect when a new source
|
||||
artifact is available.
|
||||
|
||||
### Observed Chart Name
|
||||
|
||||
The source-controller reports the last resolved chart name of the Artifact
|
||||
for the [`.spec.chart` field](#chart) in the HelmChart's
|
||||
`.status.observedChartName`. It is used to keep track of the chart and detect
|
||||
when a new chart is found.
|
||||
|
||||
### Observed Generation
|
||||
|
||||
The source-controller reports an [observed generation][typical-status-properties]
|
||||
in the HelmChart's `.status.observedGeneration`. The observed generation is the
|
||||
latest `.metadata.generation` which resulted in either a [ready state](#ready-helmchart),
|
||||
or stalled due to error it can not recover from without human
|
||||
intervention.
|
||||
|
||||
### Last Handled Reconcile At
|
||||
|
||||
The source-controller reports the last `reconcile.fluxcd.io/requestedAt`
|
||||
annotation value it acted on in the `.status.lastHandledReconcileAt` field.
|
||||
|
||||
For practical information about this field, see [triggering a
|
||||
reconcile](#triggering-a-reconcile).
|
||||
|
||||
[typical-status-properties]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
|
||||
[kstatus-spec]: https://github.com/kubernetes-sigs/cli-utils/tree/master/pkg/kstatus
|
|
@ -0,0 +1,914 @@
|
|||
# Helm Repositories
|
||||
|
||||
<!-- menuweight:40 -->
|
||||
|
||||
There are 2 [Helm repository types](#type) defined by the `HelmRepository` API:
|
||||
- Helm HTTP/S repository, which defines a Source to produce an Artifact for a Helm
|
||||
repository index YAML (`index.yaml`).
|
||||
- OCI Helm repository, which defines a source that does not produce an Artifact.
|
||||
It's a data container to store the information about the OCI repository that
|
||||
can be used by [HelmChart](helmcharts.md) to access OCI Helm charts.
|
||||
|
||||
## Examples
|
||||
|
||||
### Helm HTTP/S repository
|
||||
|
||||
The following is an example of a HelmRepository. It creates a YAML (`.yaml`)
|
||||
Artifact from the fetched Helm repository index (in this example the [podinfo
|
||||
repository](https://github.com/stefanprodan/podinfo)):
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 5m0s
|
||||
url: https://stefanprodan.github.io/podinfo
|
||||
```
|
||||
|
||||
In the above example:
|
||||
|
||||
- A HelmRepository named `podinfo` is created, indicated by the
|
||||
`.metadata.name` field.
|
||||
- The source-controller fetches the Helm repository index YAML every five
|
||||
minutes from `https://stefanprodan.github.io/podinfo`, indicated by the
|
||||
`.spec.interval` and `.spec.url` fields.
|
||||
- The digest (algorithm defaults to SHA256) of the Helm repository index after
|
||||
stable sorting the entries is used as Artifact revision, reported in-cluster
|
||||
in the `.status.artifact.revision` field.
|
||||
- When the current HelmRepository revision differs from the latest fetched
|
||||
revision, it is stored as a new Artifact.
|
||||
- The new Artifact is reported in the `.status.artifact` field.
|
||||
|
||||
You can run this example by saving the manifest into `helmrepository.yaml`.
|
||||
|
||||
1. Apply the resource on the cluster:
|
||||
|
||||
```sh
|
||||
kubectl apply -f helmrepository.yaml
|
||||
```
|
||||
|
||||
2. Run `kubectl get helmrepository` to see the HelmRepository:
|
||||
|
||||
```console
|
||||
NAME URL AGE READY STATUS
|
||||
podinfo https://stefanprodan.github.io/podinfo 4s True stored artifact for revision 'sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
|
||||
```
|
||||
|
||||
3. Run `kubectl describe helmrepository podinfo` to see the [Artifact](#artifact)
|
||||
and [Conditions](#conditions) in the HelmRepository's Status:
|
||||
|
||||
```console
|
||||
...
|
||||
Status:
|
||||
Artifact:
|
||||
Digest: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
|
||||
Last Update Time: 2022-02-04T09:55:58Z
|
||||
Path: helmrepository/default/podinfo/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
|
||||
Revision: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
|
||||
Size: 40898
|
||||
URL: http://source-controller.flux-system.svc.cluster.local./helmrepository/default/podinfo/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
|
||||
Conditions:
|
||||
Last Transition Time: 2022-02-04T09:55:58Z
|
||||
Message: stored artifact for revision 'sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
|
||||
Observed Generation: 1
|
||||
Reason: Succeeded
|
||||
Status: True
|
||||
Type: Ready
|
||||
Last Transition Time: 2022-02-04T09:55:58Z
|
||||
Message: stored artifact for revision 'sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
|
||||
Observed Generation: 1
|
||||
Reason: Succeeded
|
||||
Status: True
|
||||
Type: ArtifactInStorage
|
||||
Observed Generation: 1
|
||||
URL: http://source-controller.flux-system.svc.cluster.local./helmrepository/default/podinfo/index.yaml
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Normal NewArtifact 1m source-controller fetched index of size 30.88kB from 'https://stefanprodan.github.io/podinfo'
|
||||
```
|
||||
|
||||
### Helm OCI repository
|
||||
|
||||
The following is an example of an OCI HelmRepository.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: default
|
||||
spec:
|
||||
type: "oci"
|
||||
interval: 5m0s
|
||||
url: oci://ghcr.io/stefanprodan/charts
|
||||
```
|
||||
|
||||
In the above example:
|
||||
|
||||
- A HelmRepository named `podinfo` is created, indicated by the
|
||||
`.metadata.name` field.
|
||||
- A HelmChart that refers to this HelmRepository uses the URL in the `.spec.url`
|
||||
field to access the OCI Helm chart.
|
||||
|
||||
**NOTE:** The `.spec.interval` field is only used by the `default` Helm
|
||||
repository and is ignored for any value in `oci` Helm repository.
|
||||
|
||||
You can run this example by saving the manifest into `helmrepository.yaml`.
|
||||
|
||||
1. Apply the resource on the cluster:
|
||||
|
||||
```sh
|
||||
kubectl apply -f helmrepository.yaml
|
||||
```
|
||||
|
||||
2. Run `kubectl get helmrepository` to see the HelmRepository:
|
||||
|
||||
```console
|
||||
NAME URL AGE READY STATUS
|
||||
podinfo oci://ghcr.io/stefanprodan/charts 3m22s
|
||||
```
|
||||
|
||||
Because the OCI Helm repository is a data container, there's nothing to report
|
||||
for `READY` and `STATUS` columns above. The existence of the object can be
|
||||
considered to be ready for use.
|
||||
|
||||
## Writing a HelmRepository spec
|
||||
|
||||
As with all other Kubernetes config, a HelmRepository needs `apiVersion`,
|
||||
`kind`, and `metadata` fields. The name of a HelmRepository object must be a
|
||||
valid [DNS subdomain name](https://kubernetes.io/docs/concepts/overview/working-with-objects/names#dns-subdomain-names).
|
||||
|
||||
A HelmRepository also needs a
|
||||
[`.spec` section](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status).
|
||||
|
||||
### Type
|
||||
|
||||
`.spec.type` is an optional field that specifies the Helm repository type.
|
||||
|
||||
Possible values are `default` for a Helm HTTP/S repository, or `oci` for an OCI Helm repository.
|
||||
|
||||
### Provider
|
||||
|
||||
`.spec.provider` is an optional field that allows specifying an OIDC provider used
|
||||
for authentication purposes.
|
||||
|
||||
Supported options are:
|
||||
- `generic`
|
||||
- `aws`
|
||||
- `azure`
|
||||
- `gcp`
|
||||
|
||||
The `generic` provider can be used for public repositories or when static credentials
|
||||
are used for authentication. If you do not specify `.spec.provider`, it defaults
|
||||
to `generic`.
|
||||
|
||||
**Note**: The provider field is supported only for Helm OCI repositories. The `spec.type`
|
||||
field must be set to `oci`.
|
||||
|
||||
#### AWS
|
||||
|
||||
The `aws` provider can be used to authenticate automatically using the EKS worker
|
||||
node IAM role or IAM Role for Service Accounts (IRSA), and by extension gain access
|
||||
to ECR.
|
||||
|
||||
##### EKS Worker Node IAM Role
|
||||
|
||||
When the worker node IAM role has access to ECR, source-controller running on it
|
||||
will also have access to ECR.
|
||||
|
||||
##### IAM Role for Service Accounts (IRSA)
|
||||
|
||||
When using IRSA to enable access to ECR, add the following patch to your bootstrap
|
||||
repository, in the `flux-system/kustomization.yaml` file:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- gotk-components.yaml
|
||||
- gotk-sync.yaml
|
||||
patches:
|
||||
- patch: |
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: source-controller
|
||||
annotations:
|
||||
eks.amazonaws.com/role-arn: <role arn>
|
||||
target:
|
||||
kind: ServiceAccount
|
||||
name: source-controller
|
||||
```
|
||||
|
||||
Note that you can attach the AWS managed policy `arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly`
|
||||
to the IAM role when using IRSA.
|
||||
|
||||
#### Azure
|
||||
|
||||
The `azure` provider can be used to authenticate automatically using Workload Identity, Kubelet Managed
|
||||
Identity or Azure Active Directory pod-managed identity (aad-pod-identity), and
|
||||
by extension gain access to ACR.
|
||||
|
||||
##### Kubelet Managed Identity
|
||||
|
||||
When the kubelet managed identity has access to ACR, source-controller running on
|
||||
it will also have access to ACR.
|
||||
|
||||
**Note:** If you have more than one identity configured on the cluster, you have to specify which one to use
|
||||
by setting the `AZURE_CLIENT_ID` environment variable in the source-controller deployment.
|
||||
|
||||
If you are running into further issues, please look at the
|
||||
[troubleshooting guide](https://github.com/Azure/azure-sdk-for-go/blob/main/sdk/azidentity/TROUBLESHOOTING.md#azure-virtual-machine-managed-identity).
|
||||
|
||||
##### Azure Workload Identity
|
||||
|
||||
When using Workload Identity to enable access to ACR, add the following patch to
|
||||
your bootstrap repository, in the `flux-system/kustomization.yaml` file:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- gotk-components.yaml
|
||||
- gotk-sync.yaml
|
||||
patches:
|
||||
- patch: |-
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: source-controller
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
azure.workload.identity/client-id: <AZURE_CLIENT_ID>
|
||||
labels:
|
||||
azure.workload.identity/use: "true"
|
||||
- patch: |-
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: source-controller
|
||||
namespace: flux-system
|
||||
labels:
|
||||
azure.workload.identity/use: "true"
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
azure.workload.identity/use: "true"
|
||||
```
|
||||
|
||||
Ensure Workload Identity is properly set up on your cluster and the mutating webhook is installed.
|
||||
Create an identity that has access to ACR. Next, establish
|
||||
a federated identity between the source-controller ServiceAccount and the
|
||||
identity. Patch the source-controller Deployment and ServiceAccount as shown in the patch
|
||||
above. Please take a look at this [guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html#6-establish-federated-identity-credential-between-the-identity-and-the-service-account-issuer--subject).
|
||||
|
||||
##### Deprecated: AAD Pod Identity
|
||||
|
||||
**Warning:** The AAD Pod Identity project will be archived in
|
||||
[September 2023](https://github.com/Azure/aad-pod-identity#-announcement),
|
||||
and you are advised to use Workload Identity instead.
|
||||
|
||||
When using aad-pod-identity to enable access to ACR, add the following patch to
|
||||
your bootstrap repository, in the `flux-system/kustomization.yaml` file:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- gotk-components.yaml
|
||||
- gotk-sync.yaml
|
||||
patches:
|
||||
- patch: |
|
||||
- op: add
|
||||
path: /spec/template/metadata/labels/aadpodidbinding
|
||||
value: <identity-name>
|
||||
target:
|
||||
kind: Deployment
|
||||
name: source-controller
|
||||
```
|
||||
|
||||
When using pod-managed identity on an AKS cluster, AAD Pod Identity has to be used
|
||||
to give the `source-controller` pod access to the ACR. To do this, you have to install
|
||||
`aad-pod-identity` on your cluster, create a managed identity that has access to the
|
||||
container registry (this can also be the Kubelet identity if it has `AcrPull` role
|
||||
assignment on the ACR), create an `AzureIdentity` and `AzureIdentityBinding` that describe
|
||||
the managed identity and then label the `source-controller` deployment with the name of the
|
||||
AzureIdentity as shown in the patch above. Please take a look at [this guide](https://azure.github.io/aad-pod-identity/docs/)
|
||||
or [this one](https://docs.microsoft.com/en-us/azure/aks/use-azure-ad-pod-identity)
|
||||
if you want to use AKS pod-managed identities add-on that is in preview.
|
||||
|
||||
#### GCP
|
||||
|
||||
The `gcp` provider can be used to authenticate automatically using OAuth scopes or
|
||||
Workload Identity, and by extension gain access to GCR or Artifact Registry.
|
||||
|
||||
##### Access Scopes
|
||||
|
||||
When the GKE nodes have the appropriate OAuth scope for accessing GCR and Artifact Registry,
|
||||
source-controller running on it will also have access to them.
|
||||
|
||||
##### GKE Workload Identity
|
||||
|
||||
When using Workload Identity to enable access to GCR or Artifact Registry, add the
|
||||
following patch to your bootstrap repository, in the `flux-system/kustomization.yaml`
|
||||
file:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- gotk-components.yaml
|
||||
- gotk-sync.yaml
|
||||
patches:
|
||||
- patch: |
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: source-controller
|
||||
annotations:
|
||||
iam.gke.io/gcp-service-account: <identity-name>
|
||||
target:
|
||||
kind: ServiceAccount
|
||||
name: source-controller
|
||||
```
|
||||
|
||||
The Artifact Registry service uses the permission `artifactregistry.repositories.downloadArtifacts`
|
||||
that is located under the Artifact Registry Reader role. If you are using Google Container Registry service,
|
||||
the needed permission is instead `storage.objects.list` which can be bound as part
|
||||
of the Container Registry Service Agent role. Take a look at [this guide](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
|
||||
for more information about setting up GKE Workload Identity.
|
||||
|
||||
### Insecure
|
||||
|
||||
`.spec.insecure` is an optional field to allow connecting to an insecure (HTTP)
|
||||
container registry server, if set to `true`. The default value is `false`,
|
||||
denying insecure non-TLS connections when fetching Helm chart OCI artifacts.
|
||||
|
||||
**Note**: The insecure field is supported only for Helm OCI repositories.
|
||||
The `spec.type` field must be set to `oci`.
|
||||
|
||||
### Interval
|
||||
|
||||
**Note:** This field is ineffectual for [OCI Helm
|
||||
Repositories](#helm-oci-repository).
|
||||
|
||||
`.spec.interval` is a an optional field that specifies the interval which the
|
||||
Helm repository index must be consulted at. When not set, the default value is
|
||||
`1m`.
|
||||
|
||||
After successfully reconciling a HelmRepository object, the source-controller
|
||||
requeues the object for inspection after the specified interval. The value
|
||||
must be in a [Go recognized duration string format](https://pkg.go.dev/time#ParseDuration),
|
||||
e.g. `10m0s` to fetch the HelmRepository index YAML every 10 minutes.
|
||||
|
||||
If the `.metadata.generation` of a resource changes (due to e.g. applying a
|
||||
change to the spec), this is handled instantly outside the interval window.
|
||||
|
||||
**Note:** The controller can be configured to apply a jitter to the interval in
|
||||
order to distribute the load more evenly when multiple HelmRepository objects
|
||||
are set up with the same interval. For more information, please refer to the
|
||||
[source-controller configuration options](https://fluxcd.io/flux/components/source/options/).
|
||||
|
||||
### URL
|
||||
|
||||
`.spec.url` is a required field that depending on the [type of the HelmRepository object](#type)
|
||||
specifies the HTTP/S or OCI address of a Helm repository.
|
||||
|
||||
For OCI, the URL is expected to point to a registry repository, e.g. `oci://ghcr.io/fluxcd/source-controller`.
|
||||
|
||||
For Helm repositories which require authentication, see [Secret reference](#secret-reference).
|
||||
|
||||
### Timeout
|
||||
|
||||
**Note:** This field is not applicable to [OCI Helm
|
||||
Repositories](#helm-oci-repository).
|
||||
|
||||
`.spec.timeout` is an optional field to specify a timeout for the fetch
|
||||
operation. The value must be in a
|
||||
[Go recognized duration string format](https://pkg.go.dev/time#ParseDuration),
|
||||
e.g. `1m30s` for a timeout of one minute and thirty seconds. When not set, the
|
||||
default value is `1m`.
|
||||
|
||||
### Secret reference
|
||||
|
||||
`.spec.secretRef.name` is an optional field to specify a name reference to a
|
||||
Secret in the same namespace as the HelmRepository, containing authentication
|
||||
credentials for the repository.
|
||||
|
||||
#### Basic access authentication
|
||||
|
||||
To authenticate towards a Helm repository using basic access authentication
|
||||
(in other words: using a username and password), the referenced Secret is
|
||||
expected to contain `.data.username` and `.data.password` values.
|
||||
|
||||
For example:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: example
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 5m0s
|
||||
url: https://example.com
|
||||
secretRef:
|
||||
name: example-user
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: example-user
|
||||
namespace: default
|
||||
stringData:
|
||||
username: "user-123456"
|
||||
password: "pass-123456"
|
||||
```
|
||||
|
||||
OCI Helm repository example:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 5m0s
|
||||
url: oci://ghcr.io/my-user/my-private-repo
|
||||
type: "oci"
|
||||
secretRef:
|
||||
name: oci-creds
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: oci-creds
|
||||
namespace: default
|
||||
stringData:
|
||||
username: "user-123456"
|
||||
password: "pass-123456"
|
||||
```
|
||||
|
||||
For OCI Helm repositories, Kubernetes secrets of type [kubernetes.io/dockerconfigjson](https://kubernetes.io/docs/concepts/configuration/secret/#secret-types) are also supported.
|
||||
It is possible to create one such secret with `kubectl create secret docker-registry`
|
||||
or using the Flux CLI:
|
||||
|
||||
```yaml
|
||||
flux create secret oci ghcr-auth \
|
||||
--url=ghcr.io \
|
||||
--username=flux \
|
||||
--password=${GITHUB_PAT}
|
||||
```
|
||||
|
||||
**Warning:** Support for specifying TLS authentication data using this API has been
|
||||
deprecated. Please use [`.spec.certSecretRef`](#cert-secret-reference) instead.
|
||||
If the controller uses the secret specified by this field to configure TLS, then
|
||||
a deprecation warning will be logged.
|
||||
|
||||
### Cert secret reference
|
||||
|
||||
`.spec.certSecretRef.name` is an optional field to specify a secret containing
|
||||
TLS certificate data. The secret can contain the following keys:
|
||||
|
||||
* `tls.crt` and `tls.key`, to specify the client certificate and private key used
|
||||
for TLS client authentication. These must be used in conjunction, i.e.
|
||||
specifying one without the other will lead to an error.
|
||||
* `ca.crt`, to specify the CA certificate used to verify the server, which is
|
||||
required if the server is using a self-signed certificate.
|
||||
|
||||
If the server is using a self-signed certificate and has TLS client
|
||||
authentication enabled, all three values are required.
|
||||
|
||||
The Secret should be of type `Opaque` or `kubernetes.io/tls`. All the files in
|
||||
the Secret are expected to be [PEM-encoded][pem-encoding]. Assuming you have
|
||||
three files; `client.key`, `client.crt` and `ca.crt` for the client private key,
|
||||
client certificate and the CA certificate respectively, you can generate the
|
||||
required Secret using the `flux create secret tls` command:
|
||||
|
||||
```sh
|
||||
flux create secret tls --tls-key-file=client.key --tls-crt-file=client.crt --ca-crt-file=ca.crt
|
||||
```
|
||||
|
||||
Example usage:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: example
|
||||
namespace: default
|
||||
spec:
|
||||
interval: 5m0s
|
||||
url: https://example.com
|
||||
certSecretRef:
|
||||
name: example-tls
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: example-tls
|
||||
namespace: default
|
||||
type: kubernetes.io/tls # or Opaque
|
||||
data:
|
||||
tls.crt: <BASE64>
|
||||
tls.key: <BASE64>
|
||||
# NOTE: Can be supplied without the above values
|
||||
ca.crt: <BASE64>
|
||||
```
|
||||
|
||||
### Pass credentials
|
||||
|
||||
`.spec.passCredentials` is an optional field to allow the credentials from the
|
||||
[Secret reference](#secret-reference) to be passed on to a host that does not
|
||||
match the host as defined in URL. This may for example be required if the host
|
||||
advertised chart URLs in the index differ from the specified URL.
|
||||
|
||||
Enabling this should be done with caution, as it can potentially result in
|
||||
credentials getting stolen in a man-in-the-middle attack. This feature only applies
|
||||
to HTTP/S Helm repositories.
|
||||
|
||||
### Suspend
|
||||
|
||||
**Note:** This field is not applicable to [OCI Helm
|
||||
Repositories](#helm-oci-repository).
|
||||
|
||||
`.spec.suspend` is an optional field to suspend the reconciliation of a
|
||||
HelmRepository. When set to `true`, the controller will stop reconciling the
|
||||
HelmRepository, and changes to the resource or the Helm repository index will
|
||||
not result in a new Artifact. When the field is set to `false` or removed, it
|
||||
will resume.
|
||||
|
||||
For practical information, see
|
||||
[suspending and resuming](#suspending-and-resuming).
|
||||
|
||||
## Working with HelmRepositories
|
||||
|
||||
**Note:** This section does not apply to [OCI Helm
|
||||
Repositories](#helm-oci-repository), being a data container, once created, they
|
||||
are ready to used by [HelmCharts](helmcharts.md).
|
||||
|
||||
### Triggering a reconcile
|
||||
|
||||
To manually tell the source-controller to reconcile a HelmRepository outside the
|
||||
[specified interval window](#interval), a HelmRepository can be annotated with
|
||||
`reconcile.fluxcd.io/requestedAt: <arbitrary value>`. Annotating the resource
|
||||
queues the object for reconciliation if the `<arbitrary-value>` differs from
|
||||
the last value the controller acted on, as reported in
|
||||
[`.status.lastHandledReconcileAt`](#last-handled-reconcile-at).
|
||||
|
||||
Using `kubectl`:
|
||||
|
||||
```sh
|
||||
kubectl annotate --field-manager=flux-client-side-apply --overwrite helmrepository/<repository-name> reconcile.fluxcd.io/requestedAt="$(date +%s)"
|
||||
```
|
||||
|
||||
Using `flux`:
|
||||
|
||||
```sh
|
||||
flux reconcile source helm <repository-name>
|
||||
```
|
||||
|
||||
### Waiting for `Ready`
|
||||
|
||||
When a change is applied, it is possible to wait for the HelmRepository to
|
||||
reach a [ready state](#ready-helmrepository) using `kubectl`:
|
||||
|
||||
```sh
|
||||
kubectl wait helmrepository/<repository-name> --for=condition=ready --timeout=1m
|
||||
```
|
||||
|
||||
### Suspending and resuming
|
||||
|
||||
When you find yourself in a situation where you temporarily want to pause the
|
||||
reconciliation of a HelmRepository, you can suspend it using the
|
||||
[`.spec.suspend` field](#suspend).
|
||||
|
||||
#### Suspend a HelmRepository
|
||||
|
||||
In your YAML declaration:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: <repository-name>
|
||||
spec:
|
||||
suspend: true
|
||||
```
|
||||
|
||||
Using `kubectl`:
|
||||
|
||||
```sh
|
||||
kubectl patch helmrepository <repository-name> --field-manager=flux-client-side-apply -p '{\"spec\": {\"suspend\" : true }}'
|
||||
```
|
||||
|
||||
Using `flux`:
|
||||
|
||||
```sh
|
||||
flux suspend source helm <repository-name>
|
||||
```
|
||||
|
||||
**Note:** When a HelmRepository has an Artifact and is suspended, and this
|
||||
Artifact later disappears from the storage due to e.g. the source-controller
|
||||
Pod being evicted from a Node, this will not be reflected in the
|
||||
HelmRepository's Status until it is resumed.
|
||||
|
||||
#### Resume a HelmRepository
|
||||
|
||||
In your YAML declaration, comment out (or remove) the field:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: <repository-name>
|
||||
spec:
|
||||
# suspend: true
|
||||
```
|
||||
|
||||
**Note:** Setting the field value to `false` has the same effect as removing
|
||||
it, but does not allow for "hot patching" using e.g. `kubectl` while practicing
|
||||
GitOps; as the manually applied patch would be overwritten by the declared
|
||||
state in Git.
|
||||
|
||||
Using `kubectl`:
|
||||
|
||||
```sh
|
||||
kubectl patch helmrepository <repository-name> --field-manager=flux-client-side-apply -p '{\"spec\" : {\"suspend\" : false }}'
|
||||
```
|
||||
|
||||
Using `flux`:
|
||||
|
||||
```sh
|
||||
flux resume source helm <repository-name>
|
||||
```
|
||||
|
||||
### Debugging a HelmRepository
|
||||
|
||||
**Note:** This section does not apply to [OCI Helm
|
||||
Repositories](#helm-oci-repository), being a data container, they are static
|
||||
objects that don't require debugging if valid.
|
||||
|
||||
There are several ways to gather information about a HelmRepository for debugging
|
||||
purposes.
|
||||
|
||||
#### Describe the HelmRepository
|
||||
|
||||
Describing a HelmRepository using `kubectl describe helmrepository <repository-name>`
|
||||
displays the latest recorded information for the resource in the `Status` and
|
||||
`Events` sections:
|
||||
|
||||
```console
|
||||
...
|
||||
Status:
|
||||
...
|
||||
Conditions:
|
||||
Last Transition Time: 2022-02-04T13:41:56Z
|
||||
Message: failed to construct Helm client: scheme "invalid" not supported
|
||||
Observed Generation: 2
|
||||
Reason: Failed
|
||||
Status: True
|
||||
Type: Stalled
|
||||
Last Transition Time: 2022-02-04T13:41:56Z
|
||||
Message: failed to construct Helm client: scheme "invalid" not supported
|
||||
Observed Generation: 2
|
||||
Reason: Failed
|
||||
Status: False
|
||||
Type: Ready
|
||||
Last Transition Time: 2022-02-04T13:41:56Z
|
||||
Message: failed to construct Helm client: scheme "invalid" not supported
|
||||
Observed Generation: 2
|
||||
Reason: Failed
|
||||
Status: True
|
||||
Type: FetchFailed
|
||||
Observed Generation: 2
|
||||
URL: http://source-controller.source-system.svc.cluster.local./helmrepository/default/podinfo/index.yaml
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Warning Failed 6s source-controller failed to construct Helm client: scheme "invalid" not supported
|
||||
```
|
||||
|
||||
#### Trace emitted Events
|
||||
|
||||
To view events for specific HelmRepository(s), `kubectl events` can be used in
|
||||
combination with `--for` to list the Events for specific objects. For example,
|
||||
running
|
||||
|
||||
```sh
|
||||
kubectl events --for HelmRepository/<repository-name>
|
||||
```
|
||||
|
||||
lists
|
||||
|
||||
```console
|
||||
LAST SEEN TYPE REASON OBJECT MESSAGE
|
||||
107s Warning Failed helmrepository/<repository-name> failed to construct Helm client: scheme "invalid" not supported
|
||||
7s Normal NewArtifact helmrepository/<repository-name> fetched index of size 30.88kB from 'https://stefanprodan.github.io/podinfo'
|
||||
3s Normal ArtifactUpToDate helmrepository/<repository-name> artifact up-to-date with remote revision: 'sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
|
||||
```
|
||||
|
||||
Besides being reported in Events, the reconciliation errors are also logged by
|
||||
the controller. The Flux CLI offer commands for filtering the logs for a
|
||||
specific HelmRepository, e.g. `flux logs --level=error --kind=HelmRepository --name=<chart-name>`.
|
||||
|
||||
## HelmRepository Status
|
||||
|
||||
**Note:** This section does not apply to [OCI Helm
|
||||
Repositories](#helm-oci-repository), they do not contain any information in the
|
||||
status.
|
||||
|
||||
### Artifact
|
||||
|
||||
The HelmRepository reports the last fetched repository index as an Artifact
|
||||
object in the `.status.artifact` of the resource.
|
||||
|
||||
The Artifact file is an exact copy of the Helm repository index YAML
|
||||
(`index-<revision>.yaml`) as fetched, and can be retrieved in-cluster from the
|
||||
`.status.artifact.url` HTTP address.
|
||||
|
||||
#### Artifact example
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: <repository-name>
|
||||
status:
|
||||
artifact:
|
||||
digest: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
|
||||
lastUpdateTime: "2022-02-04T09:55:58Z"
|
||||
path: helmrepository/<namespace>/<repository-name>/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
|
||||
revision: sha256:83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
|
||||
size: 40898
|
||||
url: http://source-controller.flux-system.svc.cluster.local./helmrepository/<namespace>/<repository-name>/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
|
||||
```
|
||||
|
||||
### Conditions
|
||||
|
||||
A HelmRepository enters various states during its lifecycle, reflected as [Kubernetes
|
||||
Conditions][typical-status-properties].
|
||||
It can be [reconciling](#reconciling-helmrepository) while fetching the
|
||||
repository index, it can be [ready](#ready-helmrepository), it can
|
||||
[fail during reconciliation](#failed-helmrepository), or it can
|
||||
[stall](#stalled-helmrepository).
|
||||
|
||||
The HelmRepository API is compatible with the [kstatus
|
||||
specification][kstatus-spec],
|
||||
and reports `Reconciling` and `Stalled` conditions where applicable to
|
||||
provide better (timeout) support to solutions polling the HelmRepository to become
|
||||
`Ready`.
|
||||
|
||||
#### Reconciling HelmRepository
|
||||
|
||||
The source-controller marks a HelmRepository as _reconciling_ when one of the following
|
||||
is true:
|
||||
|
||||
- There is no current Artifact for the HelmRepository, or the reported Artifact
|
||||
is determined to have disappeared from the storage.
|
||||
- The generation of the HelmRepository is newer than the [Observed
|
||||
Generation](#observed-generation).
|
||||
- The newly fetched Artifact revision differs from the current Artifact.
|
||||
|
||||
When the HelmRepository is "reconciling", the `Ready` Condition status becomes
|
||||
`Unknown` when the controller detects drift, and the controller adds a Condition
|
||||
with the following attributes to the HelmRepository's `.status.conditions`:
|
||||
|
||||
- `type: Reconciling`
|
||||
- `status: "True"`
|
||||
- `reason: Progressing` | `reason: ProgressingWithRetry`
|
||||
|
||||
If the reconciling state is due to a new revision, it adds an additional
|
||||
Condition with the following attributes:
|
||||
|
||||
- `type: ArtifactOutdated`
|
||||
- `status: "True"`
|
||||
- `reason: NewRevision`
|
||||
|
||||
Both Conditions have a ["negative polarity"][typical-status-properties],
|
||||
and are only present on the HelmRepository while their status value is `"True"`.
|
||||
|
||||
#### Ready HelmRepository
|
||||
|
||||
The source-controller marks a HelmRepository as _ready_ when it has the following
|
||||
characteristics:
|
||||
|
||||
- The HelmRepository reports an [Artifact](#artifact).
|
||||
- The reported Artifact exists in the controller's Artifact storage.
|
||||
- The controller was able to fetch the Helm repository index using the current
|
||||
spec.
|
||||
- The revision of the reported Artifact is up-to-date with the latest
|
||||
revision of the Helm repository.
|
||||
|
||||
When the HelmRepository is "ready", the controller sets a Condition with the following
|
||||
attributes in the HelmRepository's `.status.conditions`:
|
||||
|
||||
- `type: Ready`
|
||||
- `status: "True"`
|
||||
- `reason: Succeeded`
|
||||
|
||||
This `Ready` Condition will retain a status value of `"True"` until the
|
||||
HelmRepository is marked as [reconciling](#reconciling-helmrepository), or e.g.
|
||||
a [transient error](#failed-helmrepository) occurs due to a temporary network
|
||||
issue.
|
||||
|
||||
When the HelmRepository Artifact is archived in the controller's Artifact
|
||||
storage, the controller sets a Condition with the following attributes in the
|
||||
HelmRepository's `.status.conditions`:
|
||||
|
||||
- `type: ArtifactInStorage`
|
||||
- `status: "True"`
|
||||
- `reason: Succeeded`
|
||||
|
||||
This `ArtifactInStorage` Condition will retain a status value of `"True"` until
|
||||
the Artifact in the storage no longer exists.
|
||||
|
||||
#### Failed HelmRepository
|
||||
|
||||
The source-controller may get stuck trying to produce an Artifact for a
|
||||
HelmRepository without completing. This can occur due to some of the following
|
||||
factors:
|
||||
|
||||
- The Helm repository [URL](#url) is temporarily unavailable.
|
||||
- The [Secret reference](#secret-reference) contains a reference to a
|
||||
non-existing Secret.
|
||||
- The credentials in the referenced Secret are invalid.
|
||||
- The HelmRepository spec contains a generic misconfiguration.
|
||||
- A storage related failure when storing the artifact.
|
||||
|
||||
When this happens, the controller sets the `Ready` Condition status to `False`,
|
||||
and adds a Condition with the following attributes to the HelmRepository's
|
||||
`.status.conditions`:
|
||||
|
||||
- `type: FetchFailed` | `type: StorageOperationFailed`
|
||||
- `status: "True"`
|
||||
- `reason: AuthenticationFailed` | `reason: IndexationFailed` | `reason: Failed`
|
||||
|
||||
This condition has a ["negative polarity"][typical-status-properties],
|
||||
and is only present on the HelmRepository while the status value is `"True"`.
|
||||
There may be more arbitrary values for the `reason` field to provide accurate
|
||||
reason for a condition.
|
||||
|
||||
While the HelmRepository has this Condition, the controller will continue to
|
||||
attempt to produce an Artifact for the resource with an exponential backoff,
|
||||
until it succeeds and the HelmRepository is marked as [ready](#ready-helmrepository).
|
||||
|
||||
Note that a HelmRepository can be [reconciling](#reconciling-helmrepository)
|
||||
while failing at the same time, for example due to a newly introduced
|
||||
configuration issue in the HelmRepository spec. When a reconciliation fails, the
|
||||
`Reconciling` Condition reason would be `ProgressingWithRetry`. When the
|
||||
reconciliation is performed again after the failure, the reason is updated to
|
||||
`Progressing`.
|
||||
|
||||
#### Stalled HelmRepository
|
||||
|
||||
The source-controller can mark a HelmRepository as _stalled_ when it determines
|
||||
that without changes to the spec, the reconciliation can not succeed.
|
||||
For example because a Helm repository URL with an unsupported protocol is
|
||||
specified.
|
||||
|
||||
When this happens, the controller sets the same Conditions as when it
|
||||
[fails](#failed-helmrepository), but adds another Condition with the following
|
||||
attributes to the HelmRepository's
|
||||
`.status.conditions`:
|
||||
|
||||
- `type: Stalled`
|
||||
- `status: "True"`
|
||||
- `reason: URLInvalid`
|
||||
|
||||
While the HelmRepository has this Condition, the controller will not requeue
|
||||
the resource any further, and will stop reconciling the resource until a change
|
||||
to the spec is made.
|
||||
|
||||
### Observed Generation
|
||||
|
||||
The source-controller reports an [observed generation][typical-status-properties]
|
||||
in the HelmRepository's `.status.observedGeneration`. The observed generation is
|
||||
the latest `.metadata.generation` which resulted in either a [ready state](#ready-helmrepository),
|
||||
or stalled due to error it can not recover from without human intervention.
|
||||
|
||||
### Last Handled Reconcile At
|
||||
|
||||
The source-controller reports the last `reconcile.fluxcd.io/requestedAt`
|
||||
annotation value it acted on in the `.status.lastHandledReconcileAt` field.
|
||||
|
||||
For practical information about this field, see [triggering a
|
||||
reconcile](#triggering-a-reconcile).
|
||||
|
||||
[pem-encoding]: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail
|
||||
[typical-status-properties]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
|
||||
[kstatus-spec]: https://github.com/kubernetes-sigs/cli-utils/tree/master/pkg/kstatus
|
|
@ -929,14 +929,14 @@ func TestHelmChartReconciler_buildFromHelmRepository(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "Uses artifact as build cache with observedValuesFiles",
|
||||
beforeFunc: func(obj *helmv1.HelmChart, repository *helmv1.HelmRepository) {
|
||||
beforeFunc: func(obj *sourcev1.HelmChart, repository *sourcev1.HelmRepository) {
|
||||
obj.Spec.Chart = chartName
|
||||
obj.Spec.Version = chartVersion
|
||||
obj.Status.Artifact = &sourcev1.Artifact{Path: chartName + "-" + chartVersion + ".tgz"}
|
||||
obj.Status.ObservedValuesFiles = []string{"values.yaml", "override.yaml"}
|
||||
},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertFunc: func(g *WithT, obj *helmv1.HelmChart, build chart.Build) {
|
||||
assertFunc: func(g *WithT, obj *sourcev1.HelmChart, build chart.Build) {
|
||||
g.Expect(build.Name).To(Equal(chartName))
|
||||
g.Expect(build.Version).To(Equal(chartVersion))
|
||||
g.Expect(build.Path).To(Equal(filepath.Join(serverFactory.Root(), obj.Status.Artifact.Path)))
|
||||
|
@ -965,7 +965,7 @@ func TestHelmChartReconciler_buildFromHelmRepository(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "Missing values files are an error",
|
||||
beforeFunc: func(obj *helmv1.HelmChart, repository *helmv1.HelmRepository) {
|
||||
beforeFunc: func(obj *sourcev1.HelmChart, repository *sourcev1.HelmRepository) {
|
||||
obj.Spec.Chart = chartName
|
||||
obj.Spec.ValuesFiles = []string{"missing.yaml"}
|
||||
},
|
||||
|
@ -973,14 +973,14 @@ func TestHelmChartReconciler_buildFromHelmRepository(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "All missing values files ignored",
|
||||
beforeFunc: func(obj *helmv1.HelmChart, repository *helmv1.HelmRepository) {
|
||||
beforeFunc: func(obj *sourcev1.HelmChart, repository *sourcev1.HelmRepository) {
|
||||
obj.Spec.Chart = chartName
|
||||
obj.Spec.Version = chartVersion
|
||||
obj.Spec.ValuesFiles = []string{"missing.yaml"}
|
||||
obj.Spec.IgnoreMissingValuesFiles = true
|
||||
},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertFunc: func(g *WithT, obj *helmv1.HelmChart, build chart.Build) {
|
||||
assertFunc: func(g *WithT, obj *sourcev1.HelmChart, build chart.Build) {
|
||||
g.Expect(build.Name).To(Equal(chartName))
|
||||
g.Expect(build.Version).To(Equal(chartVersion + "+0"))
|
||||
g.Expect(build.ValuesFiles).To(BeEmpty())
|
||||
|
@ -991,14 +991,14 @@ func TestHelmChartReconciler_buildFromHelmRepository(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "Partial missing values files ignored",
|
||||
beforeFunc: func(obj *helmv1.HelmChart, repository *helmv1.HelmRepository) {
|
||||
beforeFunc: func(obj *sourcev1.HelmChart, repository *sourcev1.HelmRepository) {
|
||||
obj.Spec.Chart = chartName
|
||||
obj.Spec.Version = chartVersion
|
||||
obj.Spec.ValuesFiles = []string{"values.yaml", "override.yaml", "invalid.yaml"}
|
||||
obj.Spec.IgnoreMissingValuesFiles = true
|
||||
},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertFunc: func(g *WithT, obj *helmv1.HelmChart, build chart.Build) {
|
||||
assertFunc: func(g *WithT, obj *sourcev1.HelmChart, build chart.Build) {
|
||||
g.Expect(build.Name).To(Equal(chartName))
|
||||
g.Expect(build.Version).To(Equal(chartVersion + "+0"))
|
||||
g.Expect(build.ValuesFiles).To(Equal([]string{"values.yaml", "override.yaml"}))
|
||||
|
@ -1524,7 +1524,7 @@ func TestHelmChartReconciler_buildFromTarballArtifact(t *testing.T) {
|
|||
{
|
||||
name: "Chart from storage cache with ObservedValuesFiles",
|
||||
source: *chartsArtifact.DeepCopy(),
|
||||
beforeFunc: func(obj *helmv1.HelmChart) {
|
||||
beforeFunc: func(obj *sourcev1.HelmChart) {
|
||||
obj.Spec.Chart = "testdata/charts/helmchart-0.1.0.tgz"
|
||||
obj.Status.Artifact = cachedArtifact.DeepCopy()
|
||||
obj.Status.ObservedValuesFiles = []string{"values.yaml", "override.yaml"}
|
||||
|
@ -1751,10 +1751,10 @@ func TestHelmChartReconciler_reconcileArtifact(t *testing.T) {
|
|||
{
|
||||
name: "Updates ObservedValuesFiles after creating new artifact",
|
||||
build: mockChartBuild("helmchart", "0.1.0", "testdata/charts/helmchart-0.1.0.tgz", []string{"values.yaml", "override.yaml"}),
|
||||
beforeFunc: func(obj *helmv1.HelmChart) {
|
||||
beforeFunc: func(obj *sourcev1.HelmChart) {
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "Foo", "")
|
||||
},
|
||||
afterFunc: func(t *WithT, obj *helmv1.HelmChart) {
|
||||
afterFunc: func(t *WithT, obj *sourcev1.HelmChart) {
|
||||
t.Expect(obj.GetArtifact()).ToNot(BeNil())
|
||||
t.Expect(obj.GetArtifact().Digest).To(Equal("sha256:bbdf96023c912c393b49d5238e227576ed0d20d1bb145d7476d817b80e20c11a"))
|
||||
t.Expect(obj.GetArtifact().Revision).To(Equal("0.1.0"))
|
||||
|
@ -1764,18 +1764,18 @@ func TestHelmChartReconciler_reconcileArtifact(t *testing.T) {
|
|||
},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.ArtifactInStorageCondition, helmv1.ChartPullSucceededReason, "pulled 'helmchart' chart with version '0.1.0'"),
|
||||
*conditions.TrueCondition(sourcev1.ArtifactInStorageCondition, sourcev1.ChartPullSucceededReason, "pulled 'helmchart' chart with version '0.1.0'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Updates ObservedValuesFiles with IgnoreMissingValuesFiles after creating new artifact",
|
||||
build: mockChartBuild("helmchart", "0.1.0", "testdata/charts/helmchart-0.1.0.tgz", []string{"values.yaml", "override.yaml"}),
|
||||
beforeFunc: func(obj *helmv1.HelmChart) {
|
||||
beforeFunc: func(obj *sourcev1.HelmChart) {
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "Foo", "")
|
||||
obj.Spec.ValuesFiles = []string{"values.yaml", "missing.yaml", "override.yaml"}
|
||||
obj.Spec.IgnoreMissingValuesFiles = true
|
||||
},
|
||||
afterFunc: func(t *WithT, obj *helmv1.HelmChart) {
|
||||
afterFunc: func(t *WithT, obj *sourcev1.HelmChart) {
|
||||
t.Expect(obj.GetArtifact()).ToNot(BeNil())
|
||||
t.Expect(obj.GetArtifact().Digest).To(Equal("sha256:bbdf96023c912c393b49d5238e227576ed0d20d1bb145d7476d817b80e20c11a"))
|
||||
t.Expect(obj.GetArtifact().Revision).To(Equal("0.1.0"))
|
||||
|
@ -1785,7 +1785,7 @@ func TestHelmChartReconciler_reconcileArtifact(t *testing.T) {
|
|||
},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.ArtifactInStorageCondition, helmv1.ChartPullSucceededReason, "pulled 'helmchart' chart with version '0.1.0'"),
|
||||
*conditions.TrueCondition(sourcev1.ArtifactInStorageCondition, sourcev1.ChartPullSucceededReason, "pulled 'helmchart' chart with version '0.1.0'"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue