Document HelmRepository API v1beta2 spec
Signed-off-by: Hidde Beydals <hello@hidde.co>
This commit is contained in:
parent
67d005a65e
commit
691bd50143
|
@ -28,56 +28,62 @@ import (
|
||||||
const (
|
const (
|
||||||
// HelmRepositoryKind is the string representation of a HelmRepository.
|
// HelmRepositoryKind is the string representation of a HelmRepository.
|
||||||
HelmRepositoryKind = "HelmRepository"
|
HelmRepositoryKind = "HelmRepository"
|
||||||
// HelmRepositoryURLIndexKey is the key to use for indexing HelmRepository
|
// HelmRepositoryURLIndexKey is the key used for indexing HelmRepository
|
||||||
// resources by their HelmRepositorySpec.URL.
|
// objects by their HelmRepositorySpec.URL.
|
||||||
HelmRepositoryURLIndexKey = ".metadata.helmRepositoryURL"
|
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 {
|
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
|
// +required
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
|
|
||||||
// The name of the secret containing authentication credentials for the Helm
|
// SecretRef specifies the Secret containing authentication credentials
|
||||||
// repository.
|
// for the HelmRepository.
|
||||||
// For HTTP/S basic auth the secret must contain username and
|
// For HTTP/S basic auth the secret must contain 'username' and 'password'
|
||||||
// password fields.
|
// fields.
|
||||||
// For TLS the secret must contain a certFile and keyFile, and/or
|
// For TLS the secret must contain a 'certFile' and 'keyFile', and/or
|
||||||
// caCert fields.
|
// 'caCert' fields.
|
||||||
// +optional
|
// +optional
|
||||||
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
|
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
|
||||||
|
|
||||||
// PassCredentials allows the credentials from the SecretRef to be passed on to
|
// PassCredentials allows the credentials from the SecretRef to be passed
|
||||||
// a host that does not match the host as defined in URL.
|
// 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
|
// This may be required if the host of the advertised chart URLs in the
|
||||||
// differ from the defined URL.
|
// index differ from the defined URL.
|
||||||
// Enabling this should be done with caution, as it can potentially result in
|
// Enabling this should be done with caution, as it can potentially result
|
||||||
// credentials getting stolen in a MITM-attack.
|
// in credentials getting stolen in a MITM-attack.
|
||||||
// +optional
|
// +optional
|
||||||
PassCredentials bool `json:"passCredentials,omitempty"`
|
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
|
// +required
|
||||||
Interval metav1.Duration `json:"interval"`
|
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"
|
// +kubebuilder:default:="60s"
|
||||||
// +optional
|
// +optional
|
||||||
Timeout *metav1.Duration `json:"timeout,omitempty"`
|
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
|
// +optional
|
||||||
Suspend bool `json:"suspend,omitempty"`
|
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
|
// +optional
|
||||||
AccessFrom *acl.AccessFrom `json:"accessFrom,omitempty"`
|
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 {
|
type HelmRepositoryStatus struct {
|
||||||
// ObservedGeneration is the last observed generation.
|
// ObservedGeneration is the last observed generation of the HelmRepository
|
||||||
|
// object.
|
||||||
// +optional
|
// +optional
|
||||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||||
|
|
||||||
|
@ -85,11 +91,13 @@ type HelmRepositoryStatus struct {
|
||||||
// +optional
|
// +optional
|
||||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
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
|
// +optional
|
||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
|
|
||||||
// Artifact represents the output of the last successful repository sync.
|
// Artifact represents the last successful HelmRepository reconciliation.
|
||||||
// +optional
|
// +optional
|
||||||
Artifact *Artifact `json:"artifact,omitempty"`
|
Artifact *Artifact `json:"artifact,omitempty"`
|
||||||
|
|
||||||
|
@ -97,13 +105,9 @@ type HelmRepositoryStatus struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// IndexationFailedReason represents the fact that the indexation of the given
|
// IndexationFailedReason signals that the HelmRepository index fetch
|
||||||
// Helm repository failed.
|
// failed.
|
||||||
IndexationFailedReason string = "IndexationFailed"
|
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.
|
// GetConditions returns the status conditions of the object.
|
||||||
|
@ -116,28 +120,18 @@ func (in *HelmRepository) SetConditions(conditions []metav1.Condition) {
|
||||||
in.Status.Conditions = conditions
|
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 {
|
func (in HelmRepository) GetRequeueAfter() time.Duration {
|
||||||
return in.Spec.Interval.Duration
|
return in.Spec.Interval.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInterval returns the interval at which the source is reconciled.
|
// GetArtifact returns the latest artifact from the source if present in the
|
||||||
// Deprecated: use GetRequeueAfter instead.
|
// status sub-resource.
|
||||||
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.
|
|
||||||
func (in *HelmRepository) GetArtifact() *Artifact {
|
func (in *HelmRepository) GetArtifact() *Artifact {
|
||||||
return in.Status.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
|
||||||
// +genclient:Namespaced
|
// +genclient:Namespaced
|
||||||
// +kubebuilder:storageversion
|
// +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="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",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 {
|
type HelmRepository struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
@ -159,7 +153,7 @@ type HelmRepository struct {
|
||||||
Status HelmRepositoryStatus `json:"status,omitempty"`
|
Status HelmRepositoryStatus `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HelmRepositoryList contains a list of HelmRepository
|
// HelmRepositoryList contains a list of HelmRepository objects.
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
type HelmRepositoryList struct {
|
type HelmRepositoryList struct {
|
||||||
metav1.TypeMeta `json:",inline"`
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
|
|
@ -253,7 +253,7 @@ spec:
|
||||||
name: v1beta2
|
name: v1beta2
|
||||||
schema:
|
schema:
|
||||||
openAPIV3Schema:
|
openAPIV3Schema:
|
||||||
description: HelmRepository is the Schema for the helmrepositories API
|
description: HelmRepository is the Schema for the helmrepositories API.
|
||||||
properties:
|
properties:
|
||||||
apiVersion:
|
apiVersion:
|
||||||
description: 'APIVersion defines the versioned schema of this representation
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
@ -268,11 +268,13 @@ spec:
|
||||||
metadata:
|
metadata:
|
||||||
type: object
|
type: object
|
||||||
spec:
|
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:
|
properties:
|
||||||
accessFrom:
|
accessFrom:
|
||||||
description: AccessFrom defines an Access Control List for allowing
|
description: 'AccessFrom specifies an Access Control List for allowing
|
||||||
cross-namespace references to this object.
|
cross-namespace references to this object. NOTE: Not implemented,
|
||||||
|
provisional as of https://github.com/fluxcd/flux2/pull/2092'
|
||||||
properties:
|
properties:
|
||||||
namespaceSelectors:
|
namespaceSelectors:
|
||||||
description: NamespaceSelectors is the list of namespace selectors
|
description: NamespaceSelectors is the list of namespace selectors
|
||||||
|
@ -298,7 +300,7 @@ spec:
|
||||||
- namespaceSelectors
|
- namespaceSelectors
|
||||||
type: object
|
type: object
|
||||||
interval:
|
interval:
|
||||||
description: The interval at which to check the upstream for updates.
|
description: Interval at which to check the URL for updates.
|
||||||
type: string
|
type: string
|
||||||
passCredentials:
|
passCredentials:
|
||||||
description: PassCredentials allows the credentials from the SecretRef
|
description: PassCredentials allows the credentials from the SecretRef
|
||||||
|
@ -309,10 +311,10 @@ spec:
|
||||||
getting stolen in a MITM-attack.
|
getting stolen in a MITM-attack.
|
||||||
type: boolean
|
type: boolean
|
||||||
secretRef:
|
secretRef:
|
||||||
description: The name of the secret containing authentication credentials
|
description: SecretRef specifies the Secret containing authentication
|
||||||
for the Helm repository. For HTTP/S basic auth the secret must contain
|
credentials for the HelmRepository. For HTTP/S basic auth the secret
|
||||||
username and password fields. For TLS the secret must contain a
|
must contain 'username' and 'password' fields. For TLS the secret
|
||||||
certFile and keyFile, and/or caCert fields.
|
must contain a 'certFile' and 'keyFile', and/or 'caCert' fields.
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
description: Name of the referent.
|
description: Name of the referent.
|
||||||
|
@ -321,15 +323,15 @@ spec:
|
||||||
- name
|
- name
|
||||||
type: object
|
type: object
|
||||||
suspend:
|
suspend:
|
||||||
description: This flag tells the controller to suspend the reconciliation
|
description: Suspend tells the controller to suspend the reconciliation
|
||||||
of this source.
|
of this HelmRepository.
|
||||||
type: boolean
|
type: boolean
|
||||||
timeout:
|
timeout:
|
||||||
default: 60s
|
default: 60s
|
||||||
description: The timeout of index fetching, defaults to 60s.
|
description: Timeout of the index fetch operation, defaults to 60s.
|
||||||
type: string
|
type: string
|
||||||
url:
|
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.
|
a protocol and host.
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
|
@ -339,11 +341,11 @@ spec:
|
||||||
status:
|
status:
|
||||||
default:
|
default:
|
||||||
observedGeneration: -1
|
observedGeneration: -1
|
||||||
description: HelmRepositoryStatus defines the observed state of the HelmRepository.
|
description: HelmRepositoryStatus records the observed state of the HelmRepository.
|
||||||
properties:
|
properties:
|
||||||
artifact:
|
artifact:
|
||||||
description: Artifact represents the output of the last successful
|
description: Artifact represents the last successful HelmRepository
|
||||||
repository sync.
|
reconciliation.
|
||||||
properties:
|
properties:
|
||||||
checksum:
|
checksum:
|
||||||
description: Checksum is the SHA256 checksum of the Artifact file.
|
description: Checksum is the SHA256 checksum of the Artifact file.
|
||||||
|
@ -449,11 +451,14 @@ spec:
|
||||||
be detected.
|
be detected.
|
||||||
type: string
|
type: string
|
||||||
observedGeneration:
|
observedGeneration:
|
||||||
description: ObservedGeneration is the last observed generation.
|
description: ObservedGeneration is the last observed generation of
|
||||||
|
the HelmRepository object.
|
||||||
format: int64
|
format: int64
|
||||||
type: integer
|
type: integer
|
||||||
url:
|
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: string
|
||||||
type: object
|
type: object
|
||||||
type: object
|
type: object
|
||||||
|
|
|
@ -50,9 +50,9 @@ import (
|
||||||
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
|
||||||
)
|
)
|
||||||
|
|
||||||
// helmRepoReadyConditions contains all the conditions information needed
|
// helmRepositoryReadyCondition contains the information required to summarize a
|
||||||
// for HelmRepository Ready status conditions summary calculation.
|
// v1beta2.HelmRepository Ready Condition.
|
||||||
var helmRepoReadyConditions = summarize.Conditions{
|
var helmRepositoryReadyCondition = summarize.Conditions{
|
||||||
Target: meta.ReadyCondition,
|
Target: meta.ReadyCondition,
|
||||||
Owned: []string{
|
Owned: []string{
|
||||||
sourcev1.FetchFailedCondition,
|
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=source.toolkit.fluxcd.io,resources=helmrepositories/finalizers,verbs=get;create;update;patch;delete
|
||||||
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
|
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
|
||||||
|
|
||||||
// HelmRepositoryReconciler reconciles a HelmRepository object
|
// HelmRepositoryReconciler reconciles a v1beta2.HelmRepository object.
|
||||||
type HelmRepositoryReconciler struct {
|
type HelmRepositoryReconciler struct {
|
||||||
client.Client
|
client.Client
|
||||||
kuberecorder.EventRecorder
|
kuberecorder.EventRecorder
|
||||||
|
@ -94,10 +94,11 @@ type HelmRepositoryReconcilerOptions struct {
|
||||||
MaxConcurrentReconciles int
|
MaxConcurrentReconciles int
|
||||||
}
|
}
|
||||||
|
|
||||||
// helmRepoReconcilerFunc is the function type for all the helm repository
|
// helmRepositoryReconcileFunc is the function type for all the
|
||||||
// reconciler functions. The reconciler functions are grouped together and
|
// v1beta2.HelmRepository (sub)reconcile functions. The type implementations
|
||||||
// executed serially to perform the main operation of the reconciler.
|
// are grouped and executed serially to perform the complete reconcile of the
|
||||||
type helmRepoReconcilerFunc func(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error)
|
// 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 {
|
func (r *HelmRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
return r.SetupWithManagerAndOptions(mgr, HelmRepositoryReconcilerOptions{})
|
return r.SetupWithManagerAndOptions(mgr, HelmRepositoryReconcilerOptions{})
|
||||||
|
@ -144,7 +145,7 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||||
defer func() {
|
defer func() {
|
||||||
summarizeHelper := summarize.NewHelper(r.EventRecorder, patchHelper)
|
summarizeHelper := summarize.NewHelper(r.EventRecorder, patchHelper)
|
||||||
summarizeOpts := []summarize.Option{
|
summarizeOpts := []summarize.Option{
|
||||||
summarize.WithConditions(helmRepoReadyConditions),
|
summarize.WithConditions(helmRepositoryReadyCondition),
|
||||||
summarize.WithReconcileResult(recResult),
|
summarize.WithReconcileResult(recResult),
|
||||||
summarize.WithReconcileError(retErr),
|
summarize.WithReconcileError(retErr),
|
||||||
summarize.WithIgnoreNotFound(),
|
summarize.WithIgnoreNotFound(),
|
||||||
|
@ -152,7 +153,7 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||||
summarize.RecordContextualError,
|
summarize.RecordContextualError,
|
||||||
summarize.RecordReconcileReq,
|
summarize.RecordReconcileReq,
|
||||||
),
|
),
|
||||||
summarize.WithResultBuilder(sreconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.GetInterval().Duration}),
|
summarize.WithResultBuilder(sreconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.GetRequeueAfter()}),
|
||||||
}
|
}
|
||||||
result, retErr = summarizeHelper.SummarizeAndPatch(ctx, obj, summarizeOpts...)
|
result, retErr = summarizeHelper.SummarizeAndPatch(ctx, obj, summarizeOpts...)
|
||||||
|
|
||||||
|
@ -176,7 +177,7 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconcile actual object
|
// Reconcile actual object
|
||||||
reconcilers := []helmRepoReconcilerFunc{
|
reconcilers := []helmRepositoryReconcileFunc{
|
||||||
r.reconcileStorage,
|
r.reconcileStorage,
|
||||||
r.reconcileSource,
|
r.reconcileSource,
|
||||||
r.reconcileArtifact,
|
r.reconcileArtifact,
|
||||||
|
@ -185,12 +186,10 @@ func (r *HelmRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reque
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// reconcile iterates through the sub-reconcilers and processes the source
|
// reconcile iterates through the gitRepositoryReconcileFunc tasks for the
|
||||||
// object. The sub-reconcilers are run sequentially. The result and error of
|
// object. It returns early on the first call that returns
|
||||||
// the sub-reconciliation are collected and returned. For multiple results
|
// reconcile.ResultRequeue, or produces an error.
|
||||||
// from different sub-reconcilers, the results are combined to return the
|
func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.HelmRepository, reconcilers []helmRepositoryReconcileFunc) (sreconcile.Result, error) {
|
||||||
// result with the shortest requeue period.
|
|
||||||
func (r *HelmRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.HelmRepository, reconcilers []helmRepoReconcilerFunc) (sreconcile.Result, error) {
|
|
||||||
if obj.Generation != obj.Status.ObservedGeneration {
|
if obj.Generation != obj.Status.ObservedGeneration {
|
||||||
conditions.MarkReconciling(obj, "NewGeneration", "reconciling new object generation (%d)", obj.Generation)
|
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
|
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.
|
// All Artifacts for the object except for the current one in the Status are
|
||||||
// If the artifact in the Status object of the resource disappeared from storage, it is removed from the object.
|
// garbage collected from the Storage.
|
||||||
// If the hostname of the URLs on the object do not match the current storage server hostname, they are updated.
|
// If the Artifact in the Status of the object disappeared from the Storage,
|
||||||
func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) {
|
// 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
|
// Garbage collect previous advertised artifact(s) from storage
|
||||||
_ = r.garbageCollect(ctx, obj)
|
_ = r.garbageCollect(ctx, obj)
|
||||||
|
|
||||||
|
@ -249,13 +254,14 @@ func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, obj *so
|
||||||
return sreconcile.ResultSuccess, nil
|
return sreconcile.ResultSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reconcileSource ensures the upstream Helm repository can be reached and downloaded out using the declared
|
// reconcileSource attempts to fetch the Helm repository index using the
|
||||||
// configuration, and stores a new artifact in the storage.
|
// 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
|
// When the fetch fails, it records v1beta2.FetchFailedCondition=True and
|
||||||
// (including transient errors), it records v1beta1.FetchFailedCondition=True and returns early.
|
// returns early.
|
||||||
// If the download is successful, the given artifact pointer is set to a new artifact with the available metadata, and
|
// If successful and the index is valid, any previous
|
||||||
// the index pointer is set to the newly downloaded index.
|
// 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) {
|
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
|
// Configure Helm client to access repository
|
||||||
clientOpts := []helmgetter.Option{
|
clientOpts := []helmgetter.Option{
|
||||||
|
@ -289,7 +295,11 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, obj *sou
|
||||||
Reason: sourcev1.StorageOperationFailedReason,
|
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
|
// Construct actual options
|
||||||
opts, err := getter.ClientOptionsFromSecret(tmpDir, secret)
|
opts, err := getter.ClientOptionsFromSecret(tmpDir, secret)
|
||||||
|
@ -366,14 +376,15 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, obj *sou
|
||||||
return sreconcile.ResultSuccess, nil
|
return sreconcile.ResultSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reconcileArtifact stores a new artifact in the storage, if the current observation on the object does not match the
|
// reconcileArtifact archives a new Artifact to the Storage, if the current
|
||||||
// given data.
|
// (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
|
// The inspection of the given data to the object is differed, ensuring any
|
||||||
// v1beta1.ArtifactUnavailableCondition and v1beta1.ArtifactOutdatedCondition are always deleted.
|
// stale observations like v1beta2.ArtifactOutdatedCondition are removed.
|
||||||
// If the given artifact does not differ from the object's current, it returns early.
|
// If the given Artifact does not differ from the object's current, it returns
|
||||||
// On a successful write of a new artifact, the artifact in the status of the given object is set, and the symlink in
|
// early.
|
||||||
// the storage is updated to its path.
|
// 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) {
|
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.
|
// Always restore the Ready condition in case it got removed due to a transient error.
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -437,15 +448,16 @@ func (r *HelmRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *s
|
||||||
r.eventLogf(ctx, obj, corev1.EventTypeWarning, sourcev1.StorageOperationFailedReason,
|
r.eventLogf(ctx, obj, corev1.EventTypeWarning, sourcev1.StorageOperationFailedReason,
|
||||||
"failed to update status URL symlink: %s", err)
|
"failed to update status URL symlink: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if indexURL != "" {
|
if indexURL != "" {
|
||||||
obj.Status.URL = indexURL
|
obj.Status.URL = indexURL
|
||||||
}
|
}
|
||||||
|
|
||||||
return sreconcile.ResultSuccess, nil
|
return sreconcile.ResultSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reconcileDelete handles the delete of an object. It first garbage collects all artifacts for the object from the
|
// reconcileDelete handles the deletion of the object.
|
||||||
// artifact storage, if successful, the finalizer is removed from 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) {
|
func (r *HelmRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.HelmRepository) (sreconcile.Result, error) {
|
||||||
// Garbage collect the resource's artifacts
|
// Garbage collect the resource's artifacts
|
||||||
if err := r.garbageCollect(ctx, obj); err != nil {
|
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
|
return sreconcile.ResultEmpty, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// garbageCollect performs a garbage collection for the given v1beta1.HelmRepository. It removes all but the current
|
// garbageCollect performs a garbage collection for the given object.
|
||||||
// artifact except for when the deletion timestamp is set, which will result in the removal of all artifacts for the
|
//
|
||||||
// resource.
|
// 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 {
|
func (r *HelmRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourcev1.HelmRepository) error {
|
||||||
if !obj.DeletionTimestamp.IsZero() {
|
if !obj.DeletionTimestamp.IsZero() {
|
||||||
if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// eventLog records event and logs at the same time. This log is different from
|
// eventLogf records event and logs at the same time.
|
||||||
// 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.
|
// 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{}) {
|
func (r *HelmRepositoryReconciler) eventLogf(ctx context.Context, obj runtime.Object, eventType string, reason string, messageFmt string, args ...interface{}) {
|
||||||
msg := fmt.Sprintf(messageFmt, args...)
|
msg := fmt.Sprintf(messageFmt, args...)
|
||||||
// Log and emit event.
|
// Log and emit event.
|
||||||
|
|
|
@ -93,7 +93,7 @@ func TestHelmRepositoryReconciler_Reconcile(t *testing.T) {
|
||||||
}, timeout).Should(BeTrue())
|
}, timeout).Should(BeTrue())
|
||||||
|
|
||||||
// Check if the object status is valid.
|
// 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 := status.NewChecker(testEnv.Client, testEnv.GetScheme(), condns)
|
||||||
checker.CheckErr(ctx, obj)
|
checker.CheckErr(ctx, obj)
|
||||||
|
|
||||||
|
@ -626,8 +626,8 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
|
func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
|
||||||
// Helper to build simple helmRepoReconcilerFunc with result and error.
|
// Helper to build simple helmRepositoryReconcileFunc with result and error.
|
||||||
buildReconcileFuncs := func(r sreconcile.Result, e error) helmRepoReconcilerFunc {
|
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 func(ctx context.Context, obj *sourcev1.HelmRepository, artifact *sourcev1.Artifact, repo *repository.ChartRepository) (sreconcile.Result, error) {
|
||||||
return r, e
|
return r, e
|
||||||
}
|
}
|
||||||
|
@ -637,14 +637,14 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
generation int64
|
generation int64
|
||||||
observedGeneration int64
|
observedGeneration int64
|
||||||
reconcileFuncs []helmRepoReconcilerFunc
|
reconcileFuncs []helmRepositoryReconcileFunc
|
||||||
wantResult sreconcile.Result
|
wantResult sreconcile.Result
|
||||||
wantErr bool
|
wantErr bool
|
||||||
assertConditions []metav1.Condition
|
assertConditions []metav1.Condition
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "successful reconciliations",
|
name: "successful reconciliations",
|
||||||
reconcileFuncs: []helmRepoReconcilerFunc{
|
reconcileFuncs: []helmRepositoryReconcileFunc{
|
||||||
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
||||||
},
|
},
|
||||||
wantResult: sreconcile.ResultSuccess,
|
wantResult: sreconcile.ResultSuccess,
|
||||||
|
@ -654,7 +654,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
|
||||||
name: "successful reconciliation with generation difference",
|
name: "successful reconciliation with generation difference",
|
||||||
generation: 3,
|
generation: 3,
|
||||||
observedGeneration: 2,
|
observedGeneration: 2,
|
||||||
reconcileFuncs: []helmRepoReconcilerFunc{
|
reconcileFuncs: []helmRepositoryReconcileFunc{
|
||||||
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
||||||
},
|
},
|
||||||
wantResult: sreconcile.ResultSuccess,
|
wantResult: sreconcile.ResultSuccess,
|
||||||
|
@ -665,7 +665,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "failed reconciliation",
|
name: "failed reconciliation",
|
||||||
reconcileFuncs: []helmRepoReconcilerFunc{
|
reconcileFuncs: []helmRepositoryReconcileFunc{
|
||||||
buildReconcileFuncs(sreconcile.ResultEmpty, fmt.Errorf("some error")),
|
buildReconcileFuncs(sreconcile.ResultEmpty, fmt.Errorf("some error")),
|
||||||
},
|
},
|
||||||
wantResult: sreconcile.ResultEmpty,
|
wantResult: sreconcile.ResultEmpty,
|
||||||
|
@ -673,7 +673,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "multiple object status conditions mutations",
|
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) {
|
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")
|
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "new index revision")
|
||||||
return sreconcile.ResultSuccess, nil
|
return sreconcile.ResultSuccess, nil
|
||||||
|
@ -692,7 +692,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "subrecs with one result=Requeue, no error",
|
name: "subrecs with one result=Requeue, no error",
|
||||||
reconcileFuncs: []helmRepoReconcilerFunc{
|
reconcileFuncs: []helmRepositoryReconcileFunc{
|
||||||
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
||||||
buildReconcileFuncs(sreconcile.ResultRequeue, nil),
|
buildReconcileFuncs(sreconcile.ResultRequeue, nil),
|
||||||
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
||||||
|
@ -702,7 +702,7 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "subrecs with error before result=Requeue",
|
name: "subrecs with error before result=Requeue",
|
||||||
reconcileFuncs: []helmRepoReconcilerFunc{
|
reconcileFuncs: []helmRepositoryReconcileFunc{
|
||||||
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
buildReconcileFuncs(sreconcile.ResultSuccess, nil),
|
||||||
buildReconcileFuncs(sreconcile.ResultEmpty, fmt.Errorf("some error")),
|
buildReconcileFuncs(sreconcile.ResultEmpty, fmt.Errorf("some error")),
|
||||||
buildReconcileFuncs(sreconcile.ResultRequeue, nil),
|
buildReconcileFuncs(sreconcile.ResultRequeue, nil),
|
||||||
|
|
|
@ -676,7 +676,7 @@ HelmChartStatus
|
||||||
</div>
|
</div>
|
||||||
<h3 id="source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository
|
<h3 id="source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository
|
||||||
</h3>
|
</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__scrollwrap">
|
||||||
<div class="md-typeset__table">
|
<div class="md-typeset__table">
|
||||||
<table>
|
<table>
|
||||||
|
@ -739,7 +739,8 @@ string
|
||||||
</em>
|
</em>
|
||||||
</td>
|
</td>
|
||||||
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -753,12 +754,12 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<em>(Optional)</em>
|
||||||
<p>The name of the secret containing authentication credentials for the Helm
|
<p>SecretRef specifies the Secret containing authentication credentials
|
||||||
repository.
|
for the HelmRepository.
|
||||||
For HTTP/S basic auth the secret must contain username and
|
For HTTP/S basic auth the secret must contain ‘username’ and ‘password’
|
||||||
password fields.
|
fields.
|
||||||
For TLS the secret must contain a certFile and keyFile, and/or
|
For TLS the secret must contain a ‘certFile’ and ‘keyFile’, and/or
|
||||||
caCert fields.</p>
|
‘caCert’ fields.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -770,12 +771,12 @@ bool
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<em>(Optional)</em>
|
||||||
<p>PassCredentials allows the credentials from the SecretRef to be passed on to
|
<p>PassCredentials allows the credentials from the SecretRef to be passed
|
||||||
a host that does not match the host as defined in URL.
|
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
|
This may be required if the host of the advertised chart URLs in the
|
||||||
differ from the defined URL.
|
index differ from the defined URL.
|
||||||
Enabling this should be done with caution, as it can potentially result in
|
Enabling this should be done with caution, as it can potentially result
|
||||||
credentials getting stolen in a MITM-attack.</p>
|
in credentials getting stolen in a MITM-attack.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -788,7 +789,7 @@ Kubernetes meta/v1.Duration
|
||||||
</em>
|
</em>
|
||||||
</td>
|
</td>
|
||||||
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -802,7 +803,7 @@ Kubernetes meta/v1.Duration
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -814,7 +815,8 @@ bool
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -828,7 +830,9 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
@ -1916,7 +1920,8 @@ github.com/fluxcd/pkg/apis/meta.ReconcileRequestStatus
|
||||||
(<em>Appears on:</em>
|
(<em>Appears on:</em>
|
||||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository</a>)
|
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository</a>)
|
||||||
</p>
|
</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__scrollwrap">
|
||||||
<div class="md-typeset__table">
|
<div class="md-typeset__table">
|
||||||
<table>
|
<table>
|
||||||
|
@ -1935,7 +1940,8 @@ string
|
||||||
</em>
|
</em>
|
||||||
</td>
|
</td>
|
||||||
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1949,12 +1955,12 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<em>(Optional)</em>
|
||||||
<p>The name of the secret containing authentication credentials for the Helm
|
<p>SecretRef specifies the Secret containing authentication credentials
|
||||||
repository.
|
for the HelmRepository.
|
||||||
For HTTP/S basic auth the secret must contain username and
|
For HTTP/S basic auth the secret must contain ‘username’ and ‘password’
|
||||||
password fields.
|
fields.
|
||||||
For TLS the secret must contain a certFile and keyFile, and/or
|
For TLS the secret must contain a ‘certFile’ and ‘keyFile’, and/or
|
||||||
caCert fields.</p>
|
‘caCert’ fields.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1966,12 +1972,12 @@ bool
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<em>(Optional)</em>
|
||||||
<p>PassCredentials allows the credentials from the SecretRef to be passed on to
|
<p>PassCredentials allows the credentials from the SecretRef to be passed
|
||||||
a host that does not match the host as defined in URL.
|
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
|
This may be required if the host of the advertised chart URLs in the
|
||||||
differ from the defined URL.
|
index differ from the defined URL.
|
||||||
Enabling this should be done with caution, as it can potentially result in
|
Enabling this should be done with caution, as it can potentially result
|
||||||
credentials getting stolen in a MITM-attack.</p>
|
in credentials getting stolen in a MITM-attack.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1984,7 +1990,7 @@ Kubernetes meta/v1.Duration
|
||||||
</em>
|
</em>
|
||||||
</td>
|
</td>
|
||||||
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -1998,7 +2004,7 @@ Kubernetes meta/v1.Duration
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -2010,7 +2016,8 @@ bool
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -2024,7 +2031,9 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -2037,7 +2046,7 @@ github.com/fluxcd/pkg/apis/acl.AccessFrom
|
||||||
(<em>Appears on:</em>
|
(<em>Appears on:</em>
|
||||||
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository</a>)
|
<a href="#source.toolkit.fluxcd.io/v1beta2.HelmRepository">HelmRepository</a>)
|
||||||
</p>
|
</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__scrollwrap">
|
||||||
<div class="md-typeset__table">
|
<div class="md-typeset__table">
|
||||||
<table>
|
<table>
|
||||||
|
@ -2057,7 +2066,8 @@ int64
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<em>(Optional)</em>
|
||||||
<p>ObservedGeneration is the last observed generation.</p>
|
<p>ObservedGeneration is the last observed generation of the HelmRepository
|
||||||
|
object.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -2083,7 +2093,9 @@ string
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<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 “best effort” basis, and using the precise
|
||||||
|
HelmRepositoryStatus.Artifact data is recommended.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -2097,7 +2109,7 @@ Artifact
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<em>(Optional)</em>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -6,7 +6,7 @@ This is the v1beta2 API specification for defining the desired state sources of
|
||||||
|
|
||||||
* Source kinds:
|
* Source kinds:
|
||||||
+ GitRepository
|
+ GitRepository
|
||||||
+ HelmRepository
|
+ [HelmRepository](helmrepositories.md)
|
||||||
+ HelmChart
|
+ HelmChart
|
||||||
+ [Bucket](buckets.md)
|
+ [Bucket](buckets.md)
|
||||||
|
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue