Document HelmRepository API v1beta2 spec

Signed-off-by: Hidde Beydals <hello@hidde.co>
This commit is contained in:
Hidde Beydals 2022-02-07 12:34:43 +01:00
parent 67d005a65e
commit 691bd50143
7 changed files with 721 additions and 160 deletions

View File

@ -28,56 +28,62 @@ import (
const (
// HelmRepositoryKind is the string representation of a HelmRepository.
HelmRepositoryKind = "HelmRepository"
// HelmRepositoryURLIndexKey is the key to use for indexing HelmRepository
// resources by their HelmRepositorySpec.URL.
// HelmRepositoryURLIndexKey is the key used for indexing HelmRepository
// objects by their HelmRepositorySpec.URL.
HelmRepositoryURLIndexKey = ".metadata.helmRepositoryURL"
)
// HelmRepositorySpec defines the reference to a Helm repository.
// HelmRepositorySpec specifies the required configuration to produce an
// Artifact for a Helm repository index YAML.
type HelmRepositorySpec struct {
// The Helm repository URL, a valid URL contains at least a protocol and host.
// URL of the Helm repository, a valid URL contains at least a protocol and
// host.
// +required
URL string `json:"url"`
// The name of the secret containing authentication credentials for the Helm
// repository.
// For HTTP/S basic auth the secret must contain username and
// password fields.
// For TLS the secret must contain a certFile and keyFile, and/or
// caCert fields.
// SecretRef specifies the Secret containing authentication credentials
// for the HelmRepository.
// For HTTP/S basic auth the secret must contain 'username' and 'password'
// fields.
// For TLS the secret must contain a 'certFile' and 'keyFile', and/or
// 'caCert' fields.
// +optional
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
// PassCredentials allows the credentials from the SecretRef to be passed on to
// a host that does not match the host as defined in URL.
// This may be required if the host of the advertised chart URLs in the index
// differ from the defined URL.
// Enabling this should be done with caution, as it can potentially result in
// credentials getting stolen in a MITM-attack.
// PassCredentials allows the credentials from the SecretRef to be passed
// on to a host that does not match the host as defined in URL.
// This may be required if the host of the advertised chart URLs in the
// index differ from the defined URL.
// Enabling this should be done with caution, as it can potentially result
// in credentials getting stolen in a MITM-attack.
// +optional
PassCredentials bool `json:"passCredentials,omitempty"`
// The interval at which to check the upstream for updates.
// Interval at which to check the URL for updates.
// +required
Interval metav1.Duration `json:"interval"`
// The timeout of index fetching, defaults to 60s.
// Timeout of the index fetch operation, defaults to 60s.
// +kubebuilder:default:="60s"
// +optional
Timeout *metav1.Duration `json:"timeout,omitempty"`
// This flag tells the controller to suspend the reconciliation of this source.
// Suspend tells the controller to suspend the reconciliation of this
// HelmRepository.
// +optional
Suspend bool `json:"suspend,omitempty"`
// AccessFrom defines an Access Control List for allowing cross-namespace references to this object.
// AccessFrom specifies an Access Control List for allowing cross-namespace
// references to this object.
// NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092
// +optional
AccessFrom *acl.AccessFrom `json:"accessFrom,omitempty"`
}
// HelmRepositoryStatus defines the observed state of the HelmRepository.
// HelmRepositoryStatus records the observed state of the HelmRepository.
type HelmRepositoryStatus struct {
// ObservedGeneration is the last observed generation.
// ObservedGeneration is the last observed generation of the HelmRepository
// object.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
@ -85,11 +91,13 @@ type HelmRepositoryStatus struct {
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
// URL is the fetch link for the last index fetched.
// URL is the dynamic fetch link for the latest Artifact.
// It is provided on a "best effort" basis, and using the precise
// HelmRepositoryStatus.Artifact data is recommended.
// +optional
URL string `json:"url,omitempty"`
// Artifact represents the output of the last successful repository sync.
// Artifact represents the last successful HelmRepository reconciliation.
// +optional
Artifact *Artifact `json:"artifact,omitempty"`
@ -97,13 +105,9 @@ type HelmRepositoryStatus struct {
}
const (
// IndexationFailedReason represents the fact that the indexation of the given
// Helm repository failed.
// IndexationFailedReason signals that the HelmRepository index fetch
// failed.
IndexationFailedReason string = "IndexationFailed"
// IndexationSucceededReason represents the fact that the indexation of the
// given Helm repository succeeded.
IndexationSucceededReason string = "IndexationSucceed"
)
// GetConditions returns the status conditions of the object.
@ -116,28 +120,18 @@ func (in *HelmRepository) SetConditions(conditions []metav1.Condition) {
in.Status.Conditions = conditions
}
// GetRequeueAfter returns the duration after which the source must be reconciled again.
// GetRequeueAfter returns the duration after which the source must be
// reconciled again.
func (in HelmRepository) GetRequeueAfter() time.Duration {
return in.Spec.Interval.Duration
}
// GetInterval returns the interval at which the source is reconciled.
// Deprecated: use GetRequeueAfter instead.
func (in HelmRepository) GetInterval() metav1.Duration {
return in.Spec.Interval
}
// GetArtifact returns the latest artifact from the source if present in the status sub-resource.
// GetArtifact returns the latest artifact from the source if present in the
// status sub-resource.
func (in *HelmRepository) GetArtifact() *Artifact {
return in.Status.Artifact
}
// GetStatusConditions returns a pointer to the Status.Conditions slice.
// Deprecated: use GetConditions instead.
func (in *HelmRepository) GetStatusConditions() *[]metav1.Condition {
return &in.Status.Conditions
}
// +genclient
// +genclient:Namespaced
// +kubebuilder:storageversion
@ -149,7 +143,7 @@ func (in *HelmRepository) GetStatusConditions() *[]metav1.Condition {
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// HelmRepository is the Schema for the helmrepositories API
// HelmRepository is the Schema for the helmrepositories API.
type HelmRepository struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
@ -159,7 +153,7 @@ type HelmRepository struct {
Status HelmRepositoryStatus `json:"status,omitempty"`
}
// HelmRepositoryList contains a list of HelmRepository
// HelmRepositoryList contains a list of HelmRepository objects.
// +kubebuilder:object:root=true
type HelmRepositoryList struct {
metav1.TypeMeta `json:",inline"`

View File

@ -253,7 +253,7 @@ spec:
name: v1beta2
schema:
openAPIV3Schema:
description: HelmRepository is the Schema for the helmrepositories API
description: HelmRepository is the Schema for the helmrepositories API.
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
@ -268,11 +268,13 @@ spec:
metadata:
type: object
spec:
description: HelmRepositorySpec defines the reference to a Helm repository.
description: HelmRepositorySpec specifies the required configuration to
produce an Artifact for a Helm repository index YAML.
properties:
accessFrom:
description: AccessFrom defines an Access Control List for allowing
cross-namespace references to this object.
description: 'AccessFrom specifies an Access Control List for allowing
cross-namespace references to this object. NOTE: Not implemented,
provisional as of https://github.com/fluxcd/flux2/pull/2092'
properties:
namespaceSelectors:
description: NamespaceSelectors is the list of namespace selectors
@ -298,7 +300,7 @@ spec:
- namespaceSelectors
type: object
interval:
description: The interval at which to check the upstream for updates.
description: Interval at which to check the URL for updates.
type: string
passCredentials:
description: PassCredentials allows the credentials from the SecretRef
@ -309,10 +311,10 @@ spec:
getting stolen in a MITM-attack.
type: boolean
secretRef:
description: The name of the secret containing authentication credentials
for the Helm repository. For HTTP/S basic auth the secret must contain
username and password fields. For TLS the secret must contain a
certFile and keyFile, and/or caCert fields.
description: SecretRef specifies the Secret containing authentication
credentials for the HelmRepository. For HTTP/S basic auth the secret
must contain 'username' and 'password' fields. For TLS the secret
must contain a 'certFile' and 'keyFile', and/or 'caCert' fields.
properties:
name:
description: Name of the referent.
@ -321,15 +323,15 @@ spec:
- name
type: object
suspend:
description: This flag tells the controller to suspend the reconciliation
of this source.
description: Suspend tells the controller to suspend the reconciliation
of this HelmRepository.
type: boolean
timeout:
default: 60s
description: The timeout of index fetching, defaults to 60s.
description: Timeout of the index fetch operation, defaults to 60s.
type: string
url:
description: The Helm repository URL, a valid URL contains at least
description: URL of the Helm repository, a valid URL contains at least
a protocol and host.
type: string
required:
@ -339,11 +341,11 @@ spec:
status:
default:
observedGeneration: -1
description: HelmRepositoryStatus defines the observed state of the HelmRepository.
description: HelmRepositoryStatus records the observed state of the HelmRepository.
properties:
artifact:
description: Artifact represents the output of the last successful
repository sync.
description: Artifact represents the last successful HelmRepository
reconciliation.
properties:
checksum:
description: Checksum is the SHA256 checksum of the Artifact file.
@ -449,11 +451,14 @@ spec:
be detected.
type: string
observedGeneration:
description: ObservedGeneration is the last observed generation.
description: ObservedGeneration is the last observed generation of
the HelmRepository object.
format: int64
type: integer
url:
description: URL is the fetch link for the last index fetched.
description: URL is the dynamic fetch link for the latest Artifact.
It is provided on a "best effort" basis, and using the precise HelmRepositoryStatus.Artifact
data is recommended.
type: string
type: object
type: object

View File

@ -50,9 +50,9 @@ import (
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
)
// helmRepoReadyConditions contains all the conditions information needed
// for HelmRepository Ready status conditions summary calculation.
var helmRepoReadyConditions = summarize.Conditions{
// helmRepositoryReadyCondition contains the information required to summarize a
// v1beta2.HelmRepository Ready Condition.
var helmRepositoryReadyCondition = summarize.Conditions{
Target: meta.ReadyCondition,
Owned: []string{
sourcev1.FetchFailedCondition,
@ -80,7 +80,7 @@ var helmRepoReadyConditions = summarize.Conditions{
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=helmrepositories/finalizers,verbs=get;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
// HelmRepositoryReconciler reconciles a HelmRepository object
// HelmRepositoryReconciler reconciles a v1beta2.HelmRepository object.
type HelmRepositoryReconciler struct {
client.Client
kuberecorder.EventRecorder
@ -94,10 +94,11 @@ type HelmRepositoryReconcilerOptions struct {
MaxConcurrentReconciles int
}
// helmRepoReconcilerFunc is the function type for all the helm repository
// reconciler functions. The reconciler functions are grouped together and
// executed serially to perform the main operation of the reconciler.
type helmRepoReconcilerFunc func(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error)
// helmRepositoryReconcileFunc is the function type for all the
// v1beta2.HelmRepository (sub)reconcile functions. The type implementations
// are grouped and executed serially to perform the complete reconcile of the
// object.
type helmRepositoryReconcileFunc func(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error)
func (r *HelmRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
return r.SetupWithManagerAndOptions(mgr, HelmRepositoryReconcilerOptions{})
@ -144,7 +145,7 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
defer func() {
summarizeHelper := summarize.NewHelper(r.EventRecorder, patchHelper)
summarizeOpts := []summarize.Option{
summarize.WithConditions(helmRepoReadyConditions),
summarize.WithConditions(helmRepositoryReadyCondition),
summarize.WithReconcileResult(recResult),
summarize.WithReconcileError(retErr),
summarize.WithIgnoreNotFound(),
@ -152,7 +153,7 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
summarize.RecordContextualError,
summarize.RecordReconcileReq,
),
summarize.WithResultBuilder(sreconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.GetInterval().Duration}),
summarize.WithResultBuilder(sreconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.GetRequeueAfter()}),
}
result, retErr = summarizeHelper.SummarizeAndPatch(ctx, obj, summarizeOpts...)
@ -176,7 +177,7 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}
// Reconcile actual object
reconcilers := []helmRepoReconcilerFunc{
reconcilers := []helmRepositoryReconcileFunc{
r.reconcileStorage,
r.reconcileSource,
r.reconcileArtifact,
@ -185,12 +186,10 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
return
}
// reconcile iterates through the sub-reconcilers and processes the source
// object. The sub-reconcilers are run sequentially. The result and error of
// the sub-reconciliation are collected and returned. For multiple results
// from different sub-reconcilers, the results are combined to return the
// result with the shortest requeue period.
func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.HelmRepository, reconcilers []helmRepoReconcilerFunc) (sreconcile.Result, error) {
// reconcile iterates through the gitRepositoryReconcileFunc tasks for the
// object. It returns early on the first call that returns
// reconcile.ResultRequeue, or produces an error.
func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.HelmRepository, reconcilers []helmRepositoryReconcileFunc) (sreconcile.Result, error) {
if obj.Generation != obj.Status.ObservedGeneration {
conditions.MarkReconciling(obj, "NewGeneration", "reconciling new object generation (%d)", obj.Generation)
}
@ -220,12 +219,18 @@ func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.
return res, resErr
}
// reconcileStorage ensures the current state of the storage matches the desired and previously observed state.
// reconcileStorage ensures the current state of the storage matches the
// desired and previously observed state.
//
// All artifacts for the resource except for the current one are garbage collected from the storage.
// If the artifact in the Status object of the resource disappeared from storage, it is removed from the object.
// If the hostname of the URLs on the object do not match the current storage server hostname, they are updated.
func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) {
// All Artifacts for the object except for the current one in the Status are
// garbage collected from the Storage.
// If the Artifact in the Status of the object disappeared from the Storage,
// it is removed from the object.
// If the object does not have an Artifact in its Status, a Reconciling
// condition is added.
// The hostname of any URL in the Status of the object are updated, to ensure
// they match the Storage server hostname of current runtime.
func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sourcev1.HelmRepository, _ *sourcev1.Artifact, _ *repository.ChartRepository) (sreconcile.Result, error) {
// Garbage collect previous advertised artifact(s) from storage
_ = r.garbageCollect(ctx, obj)
@ -249,13 +254,14 @@ func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, obj *so
return sreconcile.ResultSuccess, nil
}
// reconcileSource ensures the upstream Helm repository can be reached and downloaded out using the declared
// configuration, and stores a new artifact in the storage.
// reconcileSource attempts to fetch the Helm repository index using the
// specified configuration on the v1beta2.HelmRepository object.
//
// The Helm repository index is downloaded using the defined configuration, and in case of an error during this process
// (including transient errors), it records v1beta1.FetchFailedCondition=True and returns early.
// If the download is successful, the given artifact pointer is set to a new artifact with the available metadata, and
// the index pointer is set to the newly downloaded index.
// When the fetch fails, it records v1beta2.FetchFailedCondition=True and
// returns early.
// If successful and the index is valid, any previous
// v1beta2.FetchFailedCondition is removed, and the repository.ChartRepository
// pointer is set to the newly fetched index.
func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) {
// Configure Helm client to access repository
clientOpts := []helmgetter.Option{
@ -289,7 +295,11 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, obj *sou
Reason: sourcev1.StorageOperationFailedReason,
}
}
defer os.RemoveAll(tmpDir)
defer func() {
if err = os.RemoveAll(tmpDir); err != nil {
ctrl.LoggerFrom(ctx).Error(err, "failed to remove temporary directory for credentials")
}
}()
// Construct actual options
opts, err := getter.ClientOptionsFromSecret(tmpDir, secret)
@ -366,14 +376,15 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, obj *sou
return sreconcile.ResultSuccess, nil
}
// reconcileArtifact stores a new artifact in the storage, if the current observation on the object does not match the
// given data.
// reconcileArtifact archives a new Artifact to the Storage, if the current
// (Status) data on the object does not match the given.
//
// The inspection of the given data to the object is differed, ensuring any stale observations as
// v1beta1.ArtifactUnavailableCondition and v1beta1.ArtifactOutdatedCondition are always deleted.
// If the given artifact does not differ from the object's current, it returns early.
// On a successful write of a new artifact, the artifact in the status of the given object is set, and the symlink in
// the storage is updated to its path.
// The inspection of the given data to the object is differed, ensuring any
// stale observations like v1beta2.ArtifactOutdatedCondition are removed.
// If the given Artifact does not differ from the object's current, it returns
// early.
// On a successful archive, the Artifact in the Status of the object is set,
// and the symlink in the Storage is updated to its path.
func (r *HelmRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) {
// Always restore the Ready condition in case it got removed due to a transient error.
defer func() {
@ -437,15 +448,16 @@ func (r *HelmRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *s
r.eventLogf(ctx, obj, corev1.EventTypeWarning, sourcev1.StorageOperationFailedReason,
"failed to update status URL symlink: %s", err)
}
if indexURL != "" {
obj.Status.URL = indexURL
}
return sreconcile.ResultSuccess, nil
}
// reconcileDelete handles the delete of an object. It first garbage collects all artifacts for the object from the
// artifact storage, if successful, the finalizer is removed from the object.
// reconcileDelete handles the deletion of the object.
// It first garbage collects all Artifacts for the object from the Storage.
// Removing the finalizer from the object if successful.
func (r *HelmRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.HelmRepository) (sreconcile.Result, error) {
// Garbage collect the resource's artifacts
if err := r.garbageCollect(ctx, obj); err != nil {
@ -460,9 +472,11 @@ func (r *HelmRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sou
return sreconcile.ResultEmpty, nil
}
// garbageCollect performs a garbage collection for the given v1beta1.HelmRepository. It removes all but the current
// artifact except for when the deletion timestamp is set, which will result in the removal of all artifacts for the
// resource.
// garbageCollect performs a garbage collection for the given object.
//
// It removes all but the current Artifact from the Storage, unless the
// deletion timestamp on the object is set. Which will result in the
// removal of all Artifacts for the objects.
func (r *HelmRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourcev1.HelmRepository) error {
if !obj.DeletionTimestamp.IsZero() {
if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil {
@ -491,9 +505,11 @@ func (r *HelmRepositoryReconciler) garbageCollect(ctx context.Context, obj *sour
return nil
}
// eventLog records event and logs at the same time. This log is different from
// the debug log in the event recorder in the sense that this is a simple log,
// the event recorder debug log contains complete details about the event.
// eventLogf records event and logs at the same time.
//
// This log is different from the debug log in the EventRecorder, in the sense
// that this is a simple log. While the debug log contains complete details
// about the event.
func (r *HelmRepositoryReconciler) eventLogf(ctx context.Context, obj runtime.Object, eventType string, reason string, messageFmt string, args ...interface{}) {
msg := fmt.Sprintf(messageFmt, args...)
// Log and emit event.

View File

@ -93,7 +93,7 @@ func TestHelmRepositoryReconciler_Reconcile(t *testing.T) {
}, timeout).Should(BeTrue())
// Check if the object status is valid.
condns := &status.Conditions{NegativePolarity: helmRepoReadyConditions.NegativePolarity}
condns := &status.Conditions{NegativePolarity: helmRepositoryReadyCondition.NegativePolarity}
checker := status.NewChecker(testEnv.Client, testEnv.GetScheme(), condns)
checker.CheckErr(ctx, obj)
@ -626,8 +626,8 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
}
func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
// Helper to build simple helmRepoReconcilerFunc with result and error.
buildReconcileFuncs := func(r sreconcile.Result, e error) helmRepoReconcilerFunc {
// Helper to build simple helmRepositoryReconcileFunc with result and error.
buildReconcileFuncs := func(r sreconcile.Result, e error) helmRepositoryReconcileFunc {
return func(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) {
return r, e
}
@ -637,14 +637,14 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
name string
generation int64
observedGeneration int64
reconcileFuncs []helmRepoReconcilerFunc
reconcileFuncs []helmRepositoryReconcileFunc
wantResult sreconcile.Result
wantErr bool
assertConditions []metav1.Condition
}{
{
name: "successful reconciliations",
reconcileFuncs: []helmRepoReconcilerFunc{
reconcileFuncs: []helmRepositoryReconcileFunc{
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
},
wantResult: sreconcile.ResultSuccess,
@ -654,7 +654,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
name: "successful reconciliation with generation difference",
generation: 3,
observedGeneration: 2,
reconcileFuncs: []helmRepoReconcilerFunc{
reconcileFuncs: []helmRepositoryReconcileFunc{
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
},
wantResult: sreconcile.ResultSuccess,
@ -665,7 +665,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
},
{
name: "failed reconciliation",
reconcileFuncs: []helmRepoReconcilerFunc{
reconcileFuncs: []helmRepositoryReconcileFunc{
buildReconcileFuncs(sreconcile.ResultEmpty, fmt.Errorf("some error")),
},
wantResult: sreconcile.ResultEmpty,
@ -673,7 +673,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
},
{
name: "multiple object status conditions mutations",
reconcileFuncs: []helmRepoReconcilerFunc{
reconcileFuncs: []helmRepositoryReconcileFunc{
func(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) {
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "new index revision")
return sreconcile.ResultSuccess, nil
@ -692,7 +692,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
},
{
name: "subrecs with one result=Requeue, no error",
reconcileFuncs: []helmRepoReconcilerFunc{
reconcileFuncs: []helmRepositoryReconcileFunc{
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
buildReconcileFuncs(sreconcile.ResultRequeue, nil),
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
@ -702,7 +702,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
},
{
name: "subrecs with error before result=Requeue",
reconcileFuncs: []helmRepoReconcilerFunc{
reconcileFuncs: []helmRepositoryReconcileFunc{
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
buildReconcileFuncs(sreconcile.ResultEmpty, fmt.Errorf("some error")),
buildReconcileFuncs(sreconcile.ResultRequeue, nil),

View File

@ -676,7 +676,7 @@ HelmChartStatus
</div>
<h3 id="source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository
</h3>
<p>HelmRepository is the Schema for the helmrepositories API</p>
<p>HelmRepository is the Schema for the helmrepositories API.</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
@ -739,7 +739,8 @@ string
</em>
</td>
<td>
<p>The Helm repository URL, a valid URL contains at least a protocol and host.</p>
<p>URL of the Helm repository, a valid URL contains at least a protocol and
host.</p>
</td>
</tr>
<tr>
@ -753,12 +754,12 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
</td>
<td>
<em>(Optional)</em>
<p>The name of the secret containing authentication credentials for the Helm
repository.
For HTTP/S basic auth the secret must contain username and
password fields.
For TLS the secret must contain a certFile and keyFile, and/or
caCert fields.</p>
<p>SecretRef specifies the Secret containing authentication credentials
for the HelmRepository.
For HTTP/S basic auth the secret must contain &lsquo;username&rsquo; and &lsquo;password&rsquo;
fields.
For TLS the secret must contain a &lsquo;certFile&rsquo; and &lsquo;keyFile&rsquo;, and/or
&lsquo;caCert&rsquo; fields.</p>
</td>
</tr>
<tr>
@ -770,12 +771,12 @@ bool
</td>
<td>
<em>(Optional)</em>
<p>PassCredentials allows the credentials from the SecretRef to be passed on to
a host that does not match the host as defined in URL.
This may be required if the host of the advertised chart URLs in the index
differ from the defined URL.
Enabling this should be done with caution, as it can potentially result in
credentials getting stolen in a MITM-attack.</p>
<p>PassCredentials allows the credentials from the SecretRef to be passed
on to a host that does not match the host as defined in URL.
This may be required if the host of the advertised chart URLs in the
index differ from the defined URL.
Enabling this should be done with caution, as it can potentially result
in credentials getting stolen in a MITM-attack.</p>
</td>
</tr>
<tr>
@ -788,7 +789,7 @@ Kubernetes meta/v1.Duration
</em>
</td>
<td>
<p>The interval at which to check the upstream for updates.</p>
<p>Interval at which to check the URL for updates.</p>
</td>
</tr>
<tr>
@ -802,7 +803,7 @@ Kubernetes meta/v1.Duration
</td>
<td>
<em>(Optional)</em>
<p>The timeout of index fetching, defaults to 60s.</p>
<p>Timeout of the index fetch operation, defaults to 60s.</p>
</td>
</tr>
<tr>
@ -814,7 +815,8 @@ bool
</td>
<td>
<em>(Optional)</em>
<p>This flag tells the controller to suspend the reconciliation of this source.</p>
<p>Suspend tells the controller to suspend the reconciliation of this
HelmRepository.</p>
</td>
</tr>
<tr>
@ -828,7 +830,9 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
</td>
<td>
<em>(Optional)</em>
<p>AccessFrom defines an Access Control List for allowing cross-namespace references to this object.</p>
<p>AccessFrom specifies an Access Control List for allowing cross-namespace
references to this object.
NOTE: Not implemented, provisional as of <a href="https://github.com/fluxcd/flux2/pull/2092">https://github.com/fluxcd/flux2/pull/2092</a></p>
</td>
</tr>
</table>
@ -1916,7 +1920,8 @@ github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus
(<em>Appears on:</em>
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository</a>)
</p>
<p>HelmRepositorySpec defines the reference to a Helm repository.</p>
<p>HelmRepositorySpec specifies the required configuration to produce an
Artifact for a Helm repository index YAML.</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
@ -1935,7 +1940,8 @@ string
</em>
</td>
<td>
<p>The Helm repository URL, a valid URL contains at least a protocol and host.</p>
<p>URL of the Helm repository, a valid URL contains at least a protocol and
host.</p>
</td>
</tr>
<tr>
@ -1949,12 +1955,12 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
</td>
<td>
<em>(Optional)</em>
<p>The name of the secret containing authentication credentials for the Helm
repository.
For HTTP/S basic auth the secret must contain username and
password fields.
For TLS the secret must contain a certFile and keyFile, and/or
caCert fields.</p>
<p>SecretRef specifies the Secret containing authentication credentials
for the HelmRepository.
For HTTP/S basic auth the secret must contain &lsquo;username&rsquo; and &lsquo;password&rsquo;
fields.
For TLS the secret must contain a &lsquo;certFile&rsquo; and &lsquo;keyFile&rsquo;, and/or
&lsquo;caCert&rsquo; fields.</p>
</td>
</tr>
<tr>
@ -1966,12 +1972,12 @@ bool
</td>
<td>
<em>(Optional)</em>
<p>PassCredentials allows the credentials from the SecretRef to be passed on to
a host that does not match the host as defined in URL.
This may be required if the host of the advertised chart URLs in the index
differ from the defined URL.
Enabling this should be done with caution, as it can potentially result in
credentials getting stolen in a MITM-attack.</p>
<p>PassCredentials allows the credentials from the SecretRef to be passed
on to a host that does not match the host as defined in URL.
This may be required if the host of the advertised chart URLs in the
index differ from the defined URL.
Enabling this should be done with caution, as it can potentially result
in credentials getting stolen in a MITM-attack.</p>
</td>
</tr>
<tr>
@ -1984,7 +1990,7 @@ Kubernetes meta/v1.Duration
</em>
</td>
<td>
<p>The interval at which to check the upstream for updates.</p>
<p>Interval at which to check the URL for updates.</p>
</td>
</tr>
<tr>
@ -1998,7 +2004,7 @@ Kubernetes meta/v1.Duration
</td>
<td>
<em>(Optional)</em>
<p>The timeout of index fetching, defaults to 60s.</p>
<p>Timeout of the index fetch operation, defaults to 60s.</p>
</td>
</tr>
<tr>
@ -2010,7 +2016,8 @@ bool
</td>
<td>
<em>(Optional)</em>
<p>This flag tells the controller to suspend the reconciliation of this source.</p>
<p>Suspend tells the controller to suspend the reconciliation of this
HelmRepository.</p>
</td>
</tr>
<tr>
@ -2024,7 +2031,9 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
</td>
<td>
<em>(Optional)</em>
<p>AccessFrom defines an Access Control List for allowing cross-namespace references to this object.</p>
<p>AccessFrom specifies an Access Control List for allowing cross-namespace
references to this object.
NOTE: Not implemented, provisional as of <a href="https://github.com/fluxcd/flux2/pull/2092">https://github.com/fluxcd/flux2/pull/2092</a></p>
</td>
</tr>
</tbody>
@ -2037,7 +2046,7 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
(<em>Appears on:</em>
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository</a>)
</p>
<p>HelmRepositoryStatus defines the observed state of the HelmRepository.</p>
<p>HelmRepositoryStatus records the observed state of the HelmRepository.</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
@ -2057,7 +2066,8 @@ int64
</td>
<td>
<em>(Optional)</em>
<p>ObservedGeneration is the last observed generation.</p>
<p>ObservedGeneration is the last observed generation of the HelmRepository
object.</p>
</td>
</tr>
<tr>
@ -2083,7 +2093,9 @@ string
</td>
<td>
<em>(Optional)</em>
<p>URL is the fetch link for the last index fetched.</p>
<p>URL is the dynamic fetch link for the latest Artifact.
It is provided on a &ldquo;best effort&rdquo; basis, and using the precise
HelmRepositoryStatus.Artifact data is recommended.</p>
</td>
</tr>
<tr>
@ -2097,7 +2109,7 @@ Artifact
</td>
<td>
<em>(Optional)</em>
<p>Artifact represents the output of the last successful repository sync.</p>
<p>Artifact represents the last successful HelmRepository reconciliation.</p>
</td>
</tr>
<tr>

View File

@ -6,7 +6,7 @@ This is the v1beta2 API specification for defining the desired state sources of
* Source kinds:
+ GitRepository
+ HelmRepository
+ [HelmRepository](helmrepositories.md)
+ HelmChart
+ [Bucket](buckets.md)

View File

@ -0,0 +1,534 @@
# Helm Repositories
The `HelmRepository` API defines a Source to produce an Artifact for a Helm
repository index YAML (`index.yaml`).
## Example
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/v1beta2
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 SHA256 sum 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 READY STATUS AGE
podinfo https://stefanprodan.github.io/podinfo True stored artifact for revision '83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111' 4s
```
3. Run `kubectl describe helmrepository podinfo` to see the [Artifact](#artifact)
and [Conditions](#conditions) in the HelmRepository's Status:
```console
...
Status:
Artifact:
Checksum: 83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
Last Update Time: 2022-02-04T09:55:58Z
Path: helmrepository/default/podinfo/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
Revision: 83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
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 '83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
Observed Generation: 1
Reason: Succeeded
Status: True
Type: Ready
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 stored artifact for revision '83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111'
```
## 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).
### Interval
`.spec.interval` is a required field that specifies the interval which the
Helm repository index must be consulted at.
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.
### URL
`.spec.url` is a required field that specifies the HTTP/S address of the Helm
repository. For Helm repositories which require authentication, see
[Secret reference](#secret-reference).
### Timeout
`.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. The default value
is `60s`.
### 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/v1beta2
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: example
password: 123456
```
#### TLS authentication
To provide TLS credentials to use while connecting with the Helm repository,
the referenced Secret is expected to contain `.data.certFile` and
`.data.keyFile`, and/or `.data.caFile` values.
For example:
```yaml
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: example
namespace: default
spec:
interval: 5m0s
url: https://example.com
secretRef:
name: example-tls
---
apiVersion: v1
kind: Secret
metadata:
name: example-tls
namespace: default
data:
certFile: <BASE64>
keyFile: <BASE64>
# NOTE: Can be supplied without the above values
caFile: <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.
### Suspend
`.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
### 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 --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/v1beta2
kind: HelmRepository
metadata:
name: <repository-name>
spec:
suspend: true
```
Using `kubectl`:
```sh
kubectl patch helmrepository <repository-name> -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/v1beta2
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> -p '{\"spec\" : {\"suspend\" : false }}'
```
Using `flux`:
```sh
flux resume source helm <repository-name>
```
### Debugging a HelmRepository
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 get events` can be used in
combination with `--field-sector` to list the Events for specific objects.
For example, running
```sh
kubectl get events --field-selector involvedObject.kind=HelmRepository,involvedObject.name=<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> stored artifact for revision '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
### 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/v1beta2
kind: HelmRepository
metadata:
name: <repository-name>
status:
artifact:
checksum: 83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
lastUpdateTime: "2022-02-04T09:55:58Z"
path: helmrepository/<namespace>/<repository-name>/index-83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111.yaml
revision: 83a3c595163a6ff0333e0154c790383b5be441b9db632cb36da11db1c4ece111
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
`False`, and the controller adds a Condition with the following attributes to
the HelmRepository's `.status.conditions`:
- `type: Reconciling`
- `status: "True"`
- `reason: NewGeneration` | `reason: NoArtifact` | `reason: NewRevision`
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.
#### 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.
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`
- `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"`.
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.
#### 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).
[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