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 (
|
||||
// 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"`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 ‘username’ and ‘password’
|
||||
fields.
|
||||
For TLS the secret must contain a ‘certFile’ and ‘keyFile’, and/or
|
||||
‘caCert’ 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 ‘username’ and ‘password’
|
||||
fields.
|
||||
For TLS the secret must contain a ‘certFile’ and ‘keyFile’, and/or
|
||||
‘caCert’ 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 “best effort” 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>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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