gitrepo: Add more reconciler design improvements
- Remove ArtifactUnavailable condition and use Reconciling condition to convey the same. - Make Reconciling condition affect the ready condition. - Introduce summarizeAndPatch() to calculate the final status conditions and patch them. - Introduce reconcile() to iterate through the sub-reconcilers and execute them. Signed-off-by: Sunny <darkowlzz@protonmail.com>
This commit is contained in:
parent
ea45903dd4
commit
8b43f6d7a7
|
@ -18,6 +18,7 @@ package controllers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -26,7 +27,7 @@ import (
|
|||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
kerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
kuberecorder "k8s.io/client-go/tools/record"
|
||||
|
@ -40,16 +41,48 @@ import (
|
|||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
helper "github.com/fluxcd/pkg/runtime/controller"
|
||||
"github.com/fluxcd/pkg/runtime/events"
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
"github.com/fluxcd/pkg/runtime/predicates"
|
||||
"github.com/fluxcd/source-controller/pkg/sourceignore"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
serror "github.com/fluxcd/source-controller/internal/error"
|
||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||
"github.com/fluxcd/source-controller/pkg/git"
|
||||
"github.com/fluxcd/source-controller/pkg/git/strategy"
|
||||
)
|
||||
|
||||
// Status conditions owned by the GitRepository reconciler.
|
||||
var gitRepoOwnedConditions = []string{
|
||||
sourcev1.SourceVerifiedCondition,
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.IncludeUnavailableCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.ReadyCondition,
|
||||
meta.ReconcilingCondition,
|
||||
meta.StalledCondition,
|
||||
}
|
||||
|
||||
// Conditions that Ready condition is influenced by in descending order of their
|
||||
// priority.
|
||||
var gitRepoReadyDeps = []string{
|
||||
sourcev1.IncludeUnavailableCondition,
|
||||
sourcev1.SourceVerifiedCondition,
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.StalledCondition,
|
||||
meta.ReconcilingCondition,
|
||||
}
|
||||
|
||||
// Negative conditions that Ready condition is influenced by.
|
||||
var gitRepoReadyDepsNegative = []string{
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.IncludeUnavailableCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.StalledCondition,
|
||||
meta.ReconcilingCondition,
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories/finalizers,verbs=get;create;update;patch;delete
|
||||
|
@ -71,6 +104,10 @@ type GitRepositoryReconcilerOptions struct {
|
|||
DependencyRequeueInterval time.Duration
|
||||
}
|
||||
|
||||
// gitRepoReconcilerFunc is the function type for all the Git repository
|
||||
// reconciler functions.
|
||||
type gitRepoReconcilerFunc func(ctx context.Context, obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error)
|
||||
|
||||
func (r *GitRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return r.SetupWithManagerAndOptions(mgr, GitRepositoryReconcilerOptions{})
|
||||
}
|
||||
|
@ -111,74 +148,14 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
|||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
var recResult sreconcile.Result
|
||||
|
||||
// Always attempt to patch the object and status after each reconciliation
|
||||
// NOTE: This deferred block only modifies the named return error. The
|
||||
// result from the reconciliation remains the same. Any requeue attributes
|
||||
// set in the result will continue to be effective.
|
||||
defer func() {
|
||||
// Record the value of the reconciliation request, if any
|
||||
if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok {
|
||||
obj.Status.SetLastHandledReconcileRequest(v)
|
||||
}
|
||||
|
||||
// Summarize the Ready condition based on abnormalities that may have been observed.
|
||||
conditions.SetSummary(obj,
|
||||
meta.ReadyCondition,
|
||||
conditions.WithConditions(
|
||||
sourcev1.IncludeUnavailableCondition,
|
||||
sourcev1.SourceVerifiedCondition,
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
sourcev1.ArtifactUnavailableCondition,
|
||||
),
|
||||
conditions.WithNegativePolarityConditions(
|
||||
sourcev1.ArtifactUnavailableCondition,
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.IncludeUnavailableCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
),
|
||||
)
|
||||
|
||||
// Patch the object, ignoring conflicts on the conditions owned by this controller
|
||||
patchOpts := []patch.Option{
|
||||
patch.WithOwnedConditions{
|
||||
Conditions: []string{
|
||||
sourcev1.ArtifactUnavailableCondition,
|
||||
sourcev1.SourceVerifiedCondition,
|
||||
sourcev1.FetchFailedCondition,
|
||||
sourcev1.IncludeUnavailableCondition,
|
||||
sourcev1.ArtifactOutdatedCondition,
|
||||
meta.ReadyCondition,
|
||||
meta.ReconcilingCondition,
|
||||
meta.StalledCondition,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Determine if the resource is still being reconciled, or if it has stalled, and record this observation
|
||||
if retErr == nil && (result.IsZero() || !result.Requeue) {
|
||||
// We are no longer reconciling
|
||||
conditions.Delete(obj, meta.ReconcilingCondition)
|
||||
|
||||
// We have now observed this generation
|
||||
patchOpts = append(patchOpts, patch.WithStatusObservedGeneration{})
|
||||
|
||||
readyCondition := conditions.Get(obj, meta.ReadyCondition)
|
||||
switch readyCondition.Status {
|
||||
case metav1.ConditionFalse:
|
||||
// As we are no longer reconciling and the end-state is not ready, the reconciliation has stalled
|
||||
conditions.MarkStalled(obj, readyCondition.Reason, readyCondition.Message)
|
||||
case metav1.ConditionTrue:
|
||||
// As we are no longer reconciling and the end-state is ready, the reconciliation is no longer stalled
|
||||
conditions.Delete(obj, meta.StalledCondition)
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, patch the resource
|
||||
if err := patchHelper.Patch(ctx, obj, patchOpts...); err != nil {
|
||||
// Ignore patch error "not found" when the object is being deleted.
|
||||
if !obj.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
err = kerrors.FilterOut(err, func(e error) bool { return apierrors.IsNotFound(e) })
|
||||
}
|
||||
retErr = kerrors.NewAggregate([]error{retErr, err})
|
||||
}
|
||||
retErr = r.summarizeAndPatch(ctx, obj, patchHelper, recResult, retErr)
|
||||
|
||||
// Always record readiness and duration metrics
|
||||
r.Metrics.RecordReadiness(ctx, obj)
|
||||
|
@ -189,55 +166,103 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
|||
// between init and delete
|
||||
if !controllerutil.ContainsFinalizer(obj, sourcev1.SourceFinalizer) {
|
||||
controllerutil.AddFinalizer(obj, sourcev1.SourceFinalizer)
|
||||
recResult = sreconcile.ResultRequeue
|
||||
return ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
|
||||
// Examine if the object is under deletion
|
||||
if !obj.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
return r.reconcileDelete(ctx, obj)
|
||||
res, err := r.reconcileDelete(ctx, obj)
|
||||
return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, res, err)
|
||||
}
|
||||
|
||||
// Reconcile actual object
|
||||
return r.reconcile(ctx, obj)
|
||||
reconcilers := []gitRepoReconcilerFunc{
|
||||
r.reconcileStorage,
|
||||
r.reconcileSource,
|
||||
r.reconcileInclude,
|
||||
r.reconcileArtifact,
|
||||
}
|
||||
recResult, err = r.reconcile(ctx, obj, reconcilers)
|
||||
return sreconcile.BuildRuntimeResult(ctx, r.EventRecorder, obj, recResult, err)
|
||||
}
|
||||
|
||||
// reconcile steps through the actual reconciliation tasks for the object, it returns early on the first step that
|
||||
// produces an error.
|
||||
func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.GitRepository) (ctrl.Result, error) {
|
||||
// Mark the resource as under reconciliation
|
||||
conditions.MarkReconciling(obj, meta.ProgressingReason, "")
|
||||
|
||||
// Reconcile the storage data
|
||||
if result, err := r.reconcileStorage(ctx, obj); err != nil || result.IsZero() {
|
||||
return result, err
|
||||
// summarizeAndPatch analyzes the object conditions to create a summary of the
|
||||
// status conditions and patches the object with the calculated summary.
|
||||
func (r *GitRepositoryReconciler) summarizeAndPatch(ctx context.Context, obj *sourcev1.GitRepository, patchHelper *patch.Helper, res sreconcile.Result, recErr error) error {
|
||||
// Record the value of the reconciliation request if any.
|
||||
if v, ok := meta.ReconcileAnnotationValue(obj.GetAnnotations()); ok {
|
||||
obj.Status.SetLastHandledReconcileRequest(v)
|
||||
}
|
||||
|
||||
// Compute the reconcile results, obtain patch options and reconcile error.
|
||||
var patchOpts []patch.Option
|
||||
patchOpts, recErr = sreconcile.ComputeReconcileResult(obj, res, recErr, gitRepoOwnedConditions)
|
||||
|
||||
// Summarize the Ready condition based on abnormalities that may have been observed.
|
||||
conditions.SetSummary(obj,
|
||||
meta.ReadyCondition,
|
||||
conditions.WithConditions(
|
||||
gitRepoReadyDeps...,
|
||||
),
|
||||
conditions.WithNegativePolarityConditions(
|
||||
gitRepoReadyDepsNegative...,
|
||||
),
|
||||
)
|
||||
|
||||
// Finally, patch the resource.
|
||||
if err := patchHelper.Patch(ctx, obj, patchOpts...); err != nil {
|
||||
// Ignore patch error "not found" when the object is being deleted.
|
||||
if !obj.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
err = kerrors.FilterOut(err, func(e error) bool { return apierrors.IsNotFound(e) })
|
||||
}
|
||||
recErr = kerrors.NewAggregate([]error{recErr, err})
|
||||
}
|
||||
|
||||
return recErr
|
||||
}
|
||||
|
||||
// reconcile steps iterates through the actual reconciliation tasks for objec,
|
||||
// it returns early on the first step that returns ResultRequeue or produces an
|
||||
// error.
|
||||
func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.GitRepository, reconcilers []gitRepoReconcilerFunc) (sreconcile.Result, error) {
|
||||
if obj.Generation != obj.Status.ObservedGeneration {
|
||||
conditions.MarkReconciling(obj, "NewGeneration", "reconciling new generation %d", obj.Generation)
|
||||
}
|
||||
|
||||
var artifact sourcev1.Artifact
|
||||
var includes artifactSet
|
||||
|
||||
// Create temp dir for Git clone
|
||||
tmpDir, err := os.MkdirTemp("", fmt.Sprintf("%s-%s-%s-", obj.Kind, obj.Namespace, obj.Name))
|
||||
if err != nil {
|
||||
r.Eventf(obj, events.EventSeverityError, sourcev1.StorageOperationFailedReason, "Failed to create temporary directory: %s", err)
|
||||
return ctrl.Result{}, err
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("failed to create temporary directory: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Reconcile the source from upstream
|
||||
var artifact sourcev1.Artifact
|
||||
if result, err := r.reconcileSource(ctx, obj, &artifact, tmpDir); err != nil || result.IsZero() {
|
||||
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, err
|
||||
// Run the sub-reconcilers and build the result of reconciliation.
|
||||
var res sreconcile.Result
|
||||
var resErr error
|
||||
for _, rec := range reconcilers {
|
||||
recResult, err := rec(ctx, obj, &artifact, &includes, tmpDir)
|
||||
// Exit immediately on ResultRequeue.
|
||||
if recResult == sreconcile.ResultRequeue {
|
||||
return sreconcile.ResultRequeue, nil
|
||||
}
|
||||
// If an error is received, prioritize the returned results because an
|
||||
// error also means immediate requeue.
|
||||
if err != nil {
|
||||
resErr = err
|
||||
res = recResult
|
||||
break
|
||||
}
|
||||
// Prioritize requeue request in the result.
|
||||
res = sreconcile.LowestRequeuingResult(res, recResult)
|
||||
}
|
||||
|
||||
// Reconcile includes from the storage
|
||||
var includes artifactSet
|
||||
if result, err := r.reconcileInclude(ctx, obj, tmpDir); err != nil || result.IsZero() {
|
||||
return ctrl.Result{RequeueAfter: r.requeueDependency}, err
|
||||
}
|
||||
|
||||
// Reconcile the artifact to storage
|
||||
if result, err := r.reconcileArtifact(ctx, obj, artifact, includes, tmpDir); err != nil || result.IsZero() {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
|
||||
return res, resErr
|
||||
}
|
||||
|
||||
// reconcileStorage ensures the current state of the storage matches the desired and previously observed state.
|
||||
|
@ -246,9 +271,7 @@ func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.G
|
|||
// If the artifact in the Status object of the resource disappeared from storage, it is removed from the object.
|
||||
// If the object does not have an artifact in its Status object, a v1beta1.ArtifactUnavailableCondition is set.
|
||||
// If the hostname of any of the URLs on the object do not match the current storage server hostname, they are updated.
|
||||
//
|
||||
// The caller should assume a failure if an error is returned, or the Result is zero.
|
||||
func (r *GitRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sourcev1.GitRepository) (ctrl.Result, error) {
|
||||
func (r *GitRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) {
|
||||
// Garbage collect previous advertised artifact(s) from storage
|
||||
_ = r.garbageCollect(ctx, obj)
|
||||
|
||||
|
@ -260,17 +283,16 @@ func (r *GitRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sou
|
|||
|
||||
// Record that we do not have an artifact
|
||||
if obj.GetArtifact() == nil {
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactUnavailableCondition, "NoArtifact", "No artifact for resource in storage")
|
||||
return ctrl.Result{Requeue: true}, nil
|
||||
conditions.MarkReconciling(obj, "NoArtifact", "no artifact for resource in storage")
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
conditions.Delete(obj, sourcev1.ArtifactUnavailableCondition)
|
||||
|
||||
// Always update URLs to ensure hostname is up-to-date
|
||||
// TODO(hidde): we may want to send out an event only if we notice the URL has changed
|
||||
r.Storage.SetArtifactURL(obj.GetArtifact())
|
||||
obj.Status.URL = r.Storage.SetHostname(obj.Status.URL)
|
||||
|
||||
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// reconcileSource ensures the upstream Git repository can be reached and checked out using the declared configuration,
|
||||
|
@ -284,10 +306,8 @@ func (r *GitRepositoryReconciler) reconcileStorage(ctx context.Context, obj *sou
|
|||
// signature can not be verified or the verification fails, the Condition=False and it returns early.
|
||||
// If both the checkout and signature verification are successful, the given artifact pointer is set to a new artifact
|
||||
// with the available metadata.
|
||||
//
|
||||
// The caller should assume a failure if an error is returned, or the Result is zero.
|
||||
func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
|
||||
obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, dir string) (ctrl.Result, error) {
|
||||
obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) {
|
||||
// Configure authentication strategy to access the source
|
||||
var authOpts *git.AuthOptions
|
||||
var err error
|
||||
|
@ -299,12 +319,13 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
|
|||
}
|
||||
var secret corev1.Secret
|
||||
if err := r.Client.Get(ctx, name, &secret); err != nil {
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason,
|
||||
"Failed to get secret '%s': %s", name.String(), err.Error())
|
||||
r.Eventf(obj, events.EventSeverityError, sourcev1.AuthenticationFailedReason,
|
||||
"Failed to get secret '%s': %s", name.String(), err.Error())
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to get secret '%s': %w", name.String(), err),
|
||||
Reason: sourcev1.AuthenticationFailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, e.Err.Error())
|
||||
// Return error as the world as observed may change
|
||||
return ctrl.Result{}, err
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// Configure strategy with secret
|
||||
|
@ -314,12 +335,13 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
|
|||
authOpts, err = git.AuthOptionsWithoutSecret(obj.Spec.URL)
|
||||
}
|
||||
if err != nil {
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason,
|
||||
"Failed to configure auth strategy for Git implementation '%s': %s", obj.Spec.GitImplementation, err)
|
||||
r.Eventf(obj, events.EventSeverityError, sourcev1.AuthenticationFailedReason,
|
||||
"Failed to configure auth strategy for Git implementation '%s': %s", obj.Spec.GitImplementation, err)
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to configure auth strategy for Git implementation '%s': %w", obj.Spec.GitImplementation, err),
|
||||
Reason: sourcev1.AuthenticationFailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, e.Err.Error())
|
||||
// Return error as the contents of the secret may change
|
||||
return ctrl.Result{}, err
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// Configure checkout strategy
|
||||
|
@ -333,11 +355,13 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
|
|||
checkoutStrategy, err := strategy.CheckoutStrategyForImplementation(ctx,
|
||||
git.Implementation(obj.Spec.GitImplementation), checkoutOpts)
|
||||
if err != nil {
|
||||
ctrl.LoggerFrom(ctx).Error(err, fmt.Sprintf("Failed to configure checkout strategy for Git implementation '%s'", obj.Spec.GitImplementation))
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason,
|
||||
"Failed to configure checkout strategy for Git implementation '%s': %s", obj.Spec.GitImplementation, err)
|
||||
e := &serror.Stalling{
|
||||
Err: fmt.Errorf("failed to configure checkout strategy for Git implementation '%s': %w", obj.Spec.GitImplementation, err),
|
||||
Reason: sourcev1.GitOperationFailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, e.Err.Error())
|
||||
// Do not return err as recovery without changes is impossible
|
||||
return ctrl.Result{}, nil
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// Checkout HEAD of reference in object
|
||||
|
@ -345,19 +369,20 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
|
|||
defer cancel()
|
||||
commit, err := checkoutStrategy.Checkout(gitCtx, dir, obj.Spec.URL, authOpts)
|
||||
if err != nil {
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason,
|
||||
"Failed to checkout and determine revision: %s", err)
|
||||
r.Eventf(obj, events.EventSeverityError, sourcev1.GitOperationFailedReason,
|
||||
"Failed to checkout and determine revision: %s", err)
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to checkout and determine revision: %w", err),
|
||||
Reason: sourcev1.GitOperationFailedReason,
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, e.Err.Error())
|
||||
// Coin flip on transient or persistent error, return error and hope for the best
|
||||
return ctrl.Result{}, err
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
r.Eventf(obj, events.EventSeverityInfo, sourcev1.GitOperationSucceedReason,
|
||||
"Cloned repository '%s' and checked out revision '%s'", obj.Spec.URL, commit.String())
|
||||
r.eventLogf(ctx, obj, corev1.EventTypeNormal, sourcev1.GitOperationSucceedReason,
|
||||
"cloned repository '%s' and checked out revision '%s'", obj.Spec.URL, commit.String())
|
||||
conditions.Delete(obj, sourcev1.FetchFailedCondition)
|
||||
|
||||
// Verify commit signature
|
||||
if result, err := r.verifyCommitSignature(ctx, obj, *commit); err != nil || result.IsZero() {
|
||||
if result, err := r.verifyCommitSignature(ctx, obj, *commit); err != nil || result == sreconcile.ResultEmpty {
|
||||
return result, err
|
||||
}
|
||||
|
||||
|
@ -366,9 +391,11 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
|
|||
|
||||
// Mark observations about the revision on the object
|
||||
if !obj.GetArtifact().HasRevision(commit.String()) {
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", "New upstream revision '%s'", commit.String())
|
||||
message := fmt.Sprintf("new upstream revision '%s'", commit.String())
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "NewRevision", message)
|
||||
conditions.MarkReconciling(obj, "NewRevision", message)
|
||||
}
|
||||
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// reconcileArtifact archives a new artifact to the storage, if the current observation on the object does not match the
|
||||
|
@ -380,84 +407,96 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
|
|||
// Source ignore patterns are loaded, and the given directory is archived.
|
||||
// On a successful archive, the artifact and includes in the status of the given object are set, and the symlink in the
|
||||
// storage is updated to its path.
|
||||
//
|
||||
// The caller should assume a failure if an error is returned, or the Result is zero.
|
||||
func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *sourcev1.GitRepository, artifact sourcev1.Artifact, includes artifactSet, dir string) (ctrl.Result, error) {
|
||||
func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) {
|
||||
// Always restore the Ready condition in case it got removed due to a transient error
|
||||
defer func() {
|
||||
if obj.GetArtifact() != nil {
|
||||
conditions.Delete(obj, sourcev1.ArtifactUnavailableCondition)
|
||||
}
|
||||
if obj.GetArtifact().HasRevision(artifact.Revision) && !includes.Diff(obj.Status.IncludedArtifacts) {
|
||||
conditions.Delete(obj, sourcev1.ArtifactOutdatedCondition)
|
||||
conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason,
|
||||
"Stored artifact for revision '%s'", artifact.Revision)
|
||||
"stored artifact for revision '%s'", artifact.Revision)
|
||||
}
|
||||
}()
|
||||
|
||||
// The artifact is up-to-date
|
||||
if obj.GetArtifact().HasRevision(artifact.Revision) && !includes.Diff(obj.Status.IncludedArtifacts) {
|
||||
ctrl.LoggerFrom(ctx).Info(fmt.Sprintf("Already up to date, current revision '%s'", artifact.Revision))
|
||||
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
|
||||
r.eventLogf(ctx, obj, corev1.EventTypeNormal, meta.SucceededReason, "already up to date, current revision '%s'", artifact.Revision)
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// Mark reconciling because the artifact and remote source are different.
|
||||
// and they have to be reconciled.
|
||||
conditions.MarkReconciling(obj, "NewRevision", "new upstream revision '%s'", artifact.Revision)
|
||||
|
||||
// Ensure target path exists and is a directory
|
||||
if f, err := os.Stat(dir); err != nil {
|
||||
err = fmt.Errorf("failed to stat target path: %w", err)
|
||||
return ctrl.Result{}, err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to stat target path: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
return sreconcile.ResultEmpty, e
|
||||
} else if !f.IsDir() {
|
||||
err = fmt.Errorf("invalid target path: '%s' is not a directory", dir)
|
||||
return ctrl.Result{}, err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("invalid target path: '%s' is not a directory", dir),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// Ensure artifact directory exists and acquire lock
|
||||
if err := r.Storage.MkdirAll(artifact); err != nil {
|
||||
err = fmt.Errorf("failed to create artifact directory: %w", err)
|
||||
return ctrl.Result{}, err
|
||||
if err := r.Storage.MkdirAll(*artifact); err != nil {
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("failed to create artifact directory: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
unlock, err := r.Storage.Lock(artifact)
|
||||
unlock, err := r.Storage.Lock(*artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to acquire lock for artifact: %w", err)
|
||||
return ctrl.Result{}, err
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("failed to acquire lock for artifact: %w", err),
|
||||
Reason: meta.FailedReason,
|
||||
}
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
// Load ignore rules for archiving
|
||||
ps, err := sourceignore.LoadIgnorePatterns(dir, nil)
|
||||
if err != nil {
|
||||
r.Eventf(obj, events.EventSeverityError,
|
||||
"SourceIgnoreError", "Failed to load source ignore patterns from repository: %s", err)
|
||||
return ctrl.Result{}, err
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("failed to load source ignore patterns from repository: %w", err),
|
||||
Reason: "SourceIgnoreError",
|
||||
}
|
||||
}
|
||||
if obj.Spec.Ignore != nil {
|
||||
ps = append(ps, sourceignore.ReadPatterns(strings.NewReader(*obj.Spec.Ignore), nil)...)
|
||||
}
|
||||
|
||||
// Archive directory to storage
|
||||
if err := r.Storage.Archive(&artifact, dir, SourceIgnoreFilter(ps, nil)); err != nil {
|
||||
r.Eventf(obj, events.EventSeverityError, sourcev1.StorageOperationFailedReason,
|
||||
"Unable to archive artifact to storage: %s", err)
|
||||
return ctrl.Result{}, err
|
||||
if err := r.Storage.Archive(artifact, dir, SourceIgnoreFilter(ps, nil)); err != nil {
|
||||
return sreconcile.ResultEmpty, &serror.Event{
|
||||
Err: fmt.Errorf("unable to archive artifact to storage: %w", err),
|
||||
Reason: sourcev1.StorageOperationFailedReason,
|
||||
}
|
||||
}
|
||||
r.AnnotatedEventf(obj, map[string]string{
|
||||
"revision": artifact.Revision,
|
||||
"checksum": artifact.Checksum,
|
||||
}, events.EventSeverityInfo, "NewArtifact", "Stored artifact for revision '%s'", artifact.Revision)
|
||||
}, corev1.EventTypeNormal, "NewArtifact", "stored artifact for revision '%s'", artifact.Revision)
|
||||
|
||||
// Record it on the object
|
||||
obj.Status.Artifact = artifact.DeepCopy()
|
||||
obj.Status.IncludedArtifacts = includes
|
||||
obj.Status.IncludedArtifacts = *includes
|
||||
|
||||
// Update symlink on a "best effort" basis
|
||||
url, err := r.Storage.Symlink(artifact, "latest.tar.gz")
|
||||
url, err := r.Storage.Symlink(*artifact, "latest.tar.gz")
|
||||
if err != nil {
|
||||
r.Eventf(obj, events.EventSeverityError, sourcev1.StorageOperationFailedReason,
|
||||
r.eventLogf(ctx, obj, corev1.EventTypeWarning, sourcev1.StorageOperationFailedReason,
|
||||
"Failed to update status URL symlink: %s", err)
|
||||
}
|
||||
if url != "" {
|
||||
obj.Status.URL = url
|
||||
}
|
||||
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// reconcileInclude reconciles the declared includes from the object by copying their artifact (sub)contents to the
|
||||
|
@ -466,42 +505,49 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *so
|
|||
// If an include is unavailable, it marks the object with v1beta1.IncludeUnavailableCondition and returns early.
|
||||
// If the copy operations are successful, it deletes the v1beta1.IncludeUnavailableCondition from the object.
|
||||
// If the artifactSet differs from the current set, it marks the object with v1beta1.ArtifactOutdatedCondition.
|
||||
//
|
||||
// The caller should assume a failure if an error is returned, or the Result is zero.
|
||||
func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context, obj *sourcev1.GitRepository, dir string) (ctrl.Result, error) {
|
||||
func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context, obj *sourcev1.GitRepository, artifact *sourcev1.Artifact, includes *artifactSet, dir string) (sreconcile.Result, error) {
|
||||
artifacts := make(artifactSet, len(obj.Spec.Include))
|
||||
for i, incl := range obj.Spec.Include {
|
||||
// Do this first as it is much cheaper than copy operations
|
||||
toPath, err := securejoin.SecureJoin(dir, incl.GetToPath())
|
||||
if err != nil {
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "IllegalPath",
|
||||
"Path calculation for include '%s' failed: %s", incl.GitRepositoryRef.Name, err.Error())
|
||||
return ctrl.Result{}, err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("path calculation for include '%s' failed: %w", incl.GitRepositoryRef.Name, err),
|
||||
Reason: "IllegalPath",
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "IllegalPath", e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// Retrieve the included GitRepository
|
||||
dep := &sourcev1.GitRepository{}
|
||||
if err := r.Get(ctx, types.NamespacedName{Namespace: obj.Namespace, Name: incl.GitRepositoryRef.Name}, dep); err != nil {
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "NotFound",
|
||||
"Could not get resource for include '%s': %s", incl.GitRepositoryRef.Name, err.Error())
|
||||
return ctrl.Result{}, err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("could not get resource for include '%s': %w", incl.GitRepositoryRef.Name, err),
|
||||
Reason: "NotFound",
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "NotFound", e.Err.Error())
|
||||
return sreconcile.ResultEmpty, err
|
||||
}
|
||||
|
||||
// Confirm include has an artifact
|
||||
if dep.GetArtifact() == nil {
|
||||
ctrl.LoggerFrom(ctx).Error(nil, fmt.Sprintf("No artifact available for include '%s'", incl.GitRepositoryRef.Name))
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "NoArtifact",
|
||||
"No artifact available for include '%s'", incl.GitRepositoryRef.Name)
|
||||
return ctrl.Result{}, nil
|
||||
e := &serror.Stalling{
|
||||
Err: fmt.Errorf("no artifact available for include '%s'", incl.GitRepositoryRef.Name),
|
||||
Reason: "NoArtifact",
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "NoArtifact", e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
// Copy artifact (sub)contents to configured directory
|
||||
if err := r.Storage.CopyToPath(dep.GetArtifact(), incl.GetFromPath(), toPath); err != nil {
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "CopyFailure",
|
||||
"Failed to copy '%s' include from %s to %s: %s", incl.GitRepositoryRef.Name, incl.GetFromPath(), incl.GetToPath(), err.Error())
|
||||
r.Eventf(obj, events.EventSeverityError, sourcev1.IncludeUnavailableCondition,
|
||||
"Failed to copy '%s' include from %s to %s: %s", incl.GitRepositoryRef.Name, incl.GetFromPath(), incl.GetToPath(), err.Error())
|
||||
return ctrl.Result{}, err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("Failed to copy '%s' include from %s to %s: %w", incl.GitRepositoryRef.Name, incl.GetFromPath(), incl.GetToPath(), err),
|
||||
Reason: "CopyFailure",
|
||||
}
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "CopyFailure", e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
artifacts[i] = dep.GetArtifact().DeepCopy()
|
||||
}
|
||||
|
@ -511,33 +557,34 @@ func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context, obj *sou
|
|||
|
||||
// Observe if the artifacts still match the previous included ones
|
||||
if artifacts.Diff(obj.Status.IncludedArtifacts) {
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "IncludeChange", "Included artifacts differ from last observed includes")
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "IncludeChange",
|
||||
"included artifacts differ from last observed includes")
|
||||
}
|
||||
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
|
||||
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.
|
||||
func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.GitRepository) (ctrl.Result, error) {
|
||||
func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sourcev1.GitRepository) (sreconcile.Result, error) {
|
||||
// Garbage collect the resource's artifacts
|
||||
if err := r.garbageCollect(ctx, obj); err != nil {
|
||||
// Return the error so we retry the failed garbage collection
|
||||
return ctrl.Result{}, err
|
||||
return sreconcile.ResultEmpty, err
|
||||
}
|
||||
|
||||
// Remove our finalizer from the list
|
||||
controllerutil.RemoveFinalizer(obj, sourcev1.SourceFinalizer)
|
||||
|
||||
// Stop reconciliation as the object is being deleted
|
||||
return ctrl.Result{}, nil
|
||||
return sreconcile.ResultEmpty, nil
|
||||
}
|
||||
|
||||
// verifyCommitSignature verifies the signature of the given commit if a verification mode is configured on the object.
|
||||
func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj *sourcev1.GitRepository, commit git.Commit) (ctrl.Result, error) {
|
||||
func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj *sourcev1.GitRepository, commit git.Commit) (sreconcile.Result, error) {
|
||||
// Check if there is a commit verification is configured and remove any old observations if there is none
|
||||
if obj.Spec.Verification == nil || obj.Spec.Verification.Mode == "" {
|
||||
conditions.Delete(obj, sourcev1.SourceVerifiedCondition)
|
||||
return ctrl.Result{RequeueAfter: obj.GetRequeueAfter()}, nil
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// Get secret with GPG data
|
||||
|
@ -547,9 +594,12 @@ func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj
|
|||
}
|
||||
secret := &corev1.Secret{}
|
||||
if err := r.Client.Get(ctx, publicKeySecret, secret); err != nil {
|
||||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, meta.FailedReason, "PGP public keys secret error: %s", err.Error())
|
||||
r.Eventf(obj, events.EventSeverityError, "VerificationError", "PGP public keys secret error: %s", err.Error())
|
||||
return ctrl.Result{}, err
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("PGP public keys secret error: %w", err),
|
||||
Reason: "VerificationError",
|
||||
}
|
||||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, meta.FailedReason, e.Err.Error())
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
var keyRings []string
|
||||
|
@ -558,15 +608,20 @@ func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj
|
|||
}
|
||||
// Verify commit with GPG data from secret
|
||||
if _, err := commit.Verify(keyRings...); err != nil {
|
||||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, meta.FailedReason, "Signature verification of commit '%s' failed: %s", commit.Hash.String(), err)
|
||||
r.Eventf(obj, events.EventSeverityError, "InvalidCommitSignature", "Signature verification of commit '%s' failed: %s", commit.Hash.String(), err)
|
||||
e := &serror.Event{
|
||||
Err: fmt.Errorf("signature verification of commit '%s' failed: %w", commit.Hash.String(), err),
|
||||
Reason: "InvalidCommitSignature",
|
||||
}
|
||||
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, meta.FailedReason, e.Err.Error())
|
||||
// Return error in the hope the secret changes
|
||||
return ctrl.Result{}, err
|
||||
return sreconcile.ResultEmpty, e
|
||||
}
|
||||
|
||||
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason, "Verified signature of commit '%s'", commit.Hash.String())
|
||||
r.Eventf(obj, events.EventSeverityInfo, "VerifiedCommit", "Verified signature of commit '%s'", commit.Hash.String())
|
||||
return ctrl.Result{RequeueAfter: obj.Spec.Interval.Duration}, nil
|
||||
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason,
|
||||
"verified signature of commit '%s'", commit.Hash.String())
|
||||
r.eventLogf(ctx, obj, corev1.EventTypeNormal, "VerifiedCommit",
|
||||
"verified signature of commit '%s'", commit.Hash.String())
|
||||
return sreconcile.ResultSuccess, nil
|
||||
}
|
||||
|
||||
// garbageCollect performs a garbage collection for the given v1beta1.GitRepository. It removes all but the current
|
||||
|
@ -575,23 +630,40 @@ func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj
|
|||
func (r *GitRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourcev1.GitRepository) error {
|
||||
if !obj.DeletionTimestamp.IsZero() {
|
||||
if err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil {
|
||||
r.Eventf(obj, events.EventSeverityError, "GarbageCollectionFailed",
|
||||
"Garbage collection for deleted resource failed: %s", err)
|
||||
return err
|
||||
return &serror.Event{
|
||||
Err: fmt.Errorf("garbage collection for deleted resource failed: %w", err),
|
||||
Reason: "GarbageCollectionFailed",
|
||||
}
|
||||
}
|
||||
obj.Status.Artifact = nil
|
||||
// TODO(hidde): we should only push this event if we actually garbage collected something
|
||||
r.Eventf(obj, events.EventSeverityInfo, "GarbageCollectionSucceeded",
|
||||
"Garbage collected artifacts for deleted resource")
|
||||
r.eventLogf(ctx, obj, corev1.EventTypeNormal, "GarbageCollectionSucceeded",
|
||||
"garbage collected artifacts for deleted resource")
|
||||
return nil
|
||||
}
|
||||
if obj.GetArtifact() != nil {
|
||||
if err := r.Storage.RemoveAllButCurrent(*obj.GetArtifact()); err != nil {
|
||||
r.Eventf(obj, events.EventSeverityError, "GarbageCollectionFailed", "Garbage collection of old artifacts failed: %s", err)
|
||||
return err
|
||||
return &serror.Event{
|
||||
Err: fmt.Errorf("garbage collection of old artifacts failed: %w", err),
|
||||
}
|
||||
}
|
||||
// TODO(hidde): we should only push this event if we actually garbage collected something
|
||||
r.Eventf(obj, events.EventSeverityInfo, "GarbageCollectionSucceeded", "Garbage collected old artifacts")
|
||||
r.eventLogf(ctx, obj, corev1.EventTypeNormal, "GarbageCollectionSucceeded",
|
||||
"garbage collected old artifacts")
|
||||
}
|
||||
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.
|
||||
func (r *GitRepositoryReconciler) eventLogf(ctx context.Context, obj runtime.Object, eventType string, reason string, messageFmt string, args ...interface{}) {
|
||||
msg := fmt.Sprintf(messageFmt, args...)
|
||||
// Log and emit event.
|
||||
if eventType == corev1.EventTypeWarning {
|
||||
ctrl.LoggerFrom(ctx).Error(errors.New(reason), msg)
|
||||
} else {
|
||||
ctrl.LoggerFrom(ctx).Info(msg)
|
||||
}
|
||||
r.Eventf(obj, eventType, reason, msg)
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/darkowlzz/controller-check/status"
|
||||
"github.com/go-git/go-billy/v5/memfs"
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
|
@ -53,6 +54,7 @@ import (
|
|||
"github.com/fluxcd/pkg/testserver"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
|
||||
"github.com/fluxcd/source-controller/pkg/git"
|
||||
)
|
||||
|
||||
|
@ -189,6 +191,11 @@ func TestGitRepositoryReconciler_Reconcile(t *testing.T) {
|
|||
obj.Generation == obj.Status.ObservedGeneration
|
||||
}, timeout).Should(BeTrue())
|
||||
|
||||
// Check if the object status is valid.
|
||||
condns := &status.Conditions{NegativePolarity: gitRepoReadyDepsNegative}
|
||||
checker := status.NewChecker(testEnv.Client, testEnv.GetScheme(), condns)
|
||||
checker.CheckErr(ctx, obj)
|
||||
|
||||
g.Expect(testEnv.Delete(ctx, obj)).To(Succeed())
|
||||
|
||||
// Wait for GitRepository to be deleted
|
||||
|
@ -216,16 +223,17 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
|
|||
server options
|
||||
secret *corev1.Secret
|
||||
beforeFunc func(obj *sourcev1.GitRepository)
|
||||
want ctrl.Result
|
||||
want sreconcile.Result
|
||||
wantErr bool
|
||||
assertConditions []metav1.Condition
|
||||
}{
|
||||
{
|
||||
name: "HTTP without secretRef makes ArtifactOutdated=True",
|
||||
protocol: "http",
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "New upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -247,9 +255,10 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
|
|||
beforeFunc: func(obj *sourcev1.GitRepository) {
|
||||
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "basic-auth"}
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "New upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -271,9 +280,10 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
|
|||
beforeFunc: func(obj *sourcev1.GitRepository) {
|
||||
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "ca-file"}
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "New upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -323,7 +333,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
|
|||
},
|
||||
wantErr: true,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, "Failed to checkout and determine revision: unable to clone: Certificate"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.GitOperationFailedReason, "failed to checkout and determine revision: unable to clone '<url>': Certificate"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -344,9 +354,10 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
|
|||
beforeFunc: func(obj *sourcev1.GitRepository) {
|
||||
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "private-key"}
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "New upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -368,9 +379,10 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
|
|||
beforeFunc: func(obj *sourcev1.GitRepository) {
|
||||
obj.Spec.SecretRef = &meta.LocalObjectReference{Name: "private-key"}
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "New upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'master/<commit>'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -384,7 +396,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
|
|||
},
|
||||
wantErr: true,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "Failed to get secret '/non-existing': secrets \"non-existing\" not found"),
|
||||
*conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.AuthenticationFailedReason, "failed to get secret '/non-existing': secrets \"non-existing\" not found"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -486,9 +498,10 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
|
|||
}
|
||||
|
||||
var artifact sourcev1.Artifact
|
||||
var includes artifactSet
|
||||
dlog := log.NewDelegatingLogSink(log.NullLogSink{})
|
||||
nullLogger := logr.New(dlog)
|
||||
got, err := r.reconcileSource(logr.NewContext(ctx, nullLogger), obj, &artifact, tmpDir)
|
||||
got, err := r.reconcileSource(logr.NewContext(ctx, nullLogger), obj, &artifact, &includes, tmpDir)
|
||||
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions(tt.assertConditions))
|
||||
g.Expect(err != nil).To(Equal(tt.wantErr))
|
||||
g.Expect(got).To(Equal(tt.want))
|
||||
|
@ -509,13 +522,13 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
|
|||
name string
|
||||
skipForImplementation string
|
||||
reference *sourcev1.GitRepositoryRef
|
||||
want ctrl.Result
|
||||
want sreconcile.Result
|
||||
wantErr bool
|
||||
wantRevision string
|
||||
}{
|
||||
{
|
||||
name: "Nil reference (default branch)",
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
wantRevision: "master/<commit>",
|
||||
},
|
||||
{
|
||||
|
@ -523,7 +536,7 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
|
|||
reference: &sourcev1.GitRepositoryRef{
|
||||
Branch: "staging",
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
wantRevision: "staging/<commit>",
|
||||
},
|
||||
{
|
||||
|
@ -531,7 +544,7 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
|
|||
reference: &sourcev1.GitRepositoryRef{
|
||||
Tag: "v0.1.0",
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
wantRevision: "v0.1.0/<commit>",
|
||||
},
|
||||
{
|
||||
|
@ -541,7 +554,7 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
|
|||
Branch: "staging",
|
||||
Commit: "<commit>",
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
wantRevision: "staging/<commit>",
|
||||
},
|
||||
{
|
||||
|
@ -551,7 +564,7 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
|
|||
Branch: "staging",
|
||||
Commit: "<commit>",
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
wantRevision: "HEAD/<commit>",
|
||||
},
|
||||
{
|
||||
|
@ -559,7 +572,7 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
|
|||
reference: &sourcev1.GitRepositoryRef{
|
||||
SemVer: "*",
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
wantRevision: "v2.0.0/<commit>",
|
||||
},
|
||||
{
|
||||
|
@ -567,7 +580,7 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
|
|||
reference: &sourcev1.GitRepositoryRef{
|
||||
SemVer: "<v0.2.1",
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
wantRevision: "0.2.0/<commit>",
|
||||
},
|
||||
{
|
||||
|
@ -576,7 +589,7 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
|
|||
SemVer: ">=1.0.0-0 <1.1.0-0",
|
||||
},
|
||||
wantRevision: "v1.0.0-alpha/<commit>",
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -641,7 +654,8 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
|
|||
obj.Spec.GitImplementation = i
|
||||
|
||||
var artifact sourcev1.Artifact
|
||||
got, err := r.reconcileSource(ctx, obj, &artifact, tmpDir)
|
||||
var includes artifactSet
|
||||
got, err := r.reconcileSource(ctx, obj, &artifact, &includes, tmpDir)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
|
@ -665,7 +679,7 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
|
|||
includes artifactSet
|
||||
beforeFunc func(obj *sourcev1.GitRepository)
|
||||
afterFunc func(t *WithT, obj *sourcev1.GitRepository, artifact sourcev1.Artifact)
|
||||
want ctrl.Result
|
||||
want sreconcile.Result
|
||||
wantErr bool
|
||||
assertConditions []metav1.Condition
|
||||
}{
|
||||
|
@ -679,9 +693,10 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
|
|||
t.Expect(obj.GetArtifact()).ToNot(BeNil())
|
||||
t.Expect(obj.Status.URL).ToNot(BeEmpty())
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'main/revision'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -697,9 +712,10 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
|
|||
t.Expect(obj.Status.IncludedArtifacts).ToNot(BeEmpty())
|
||||
t.Expect(obj.Status.URL).ToNot(BeEmpty())
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'main/revision'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -714,9 +730,9 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
|
|||
afterFunc: func(t *WithT, obj *sourcev1.GitRepository, artifact sourcev1.Artifact) {
|
||||
t.Expect(obj.Status.URL).To(BeEmpty())
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "stored artifact for revision 'main/revision'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -730,26 +746,10 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
|
|||
t.Expect(obj.GetArtifact()).ToNot(BeNil())
|
||||
t.Expect(obj.GetArtifact().Checksum).To(Equal("dc95ae14c19d335b693bbba58ae2a562242b0cf33893baffd1b7605ba578e0d6"))
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Stored artifact for revision 'main/revision'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Removes ArtifactUnavailableCondition after creating artifact",
|
||||
dir: "testdata/git/repository",
|
||||
beforeFunc: func(obj *sourcev1.GitRepository) {
|
||||
obj.Spec.Interval = metav1.Duration{Duration: interval}
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactUnavailableCondition, "Foo", "")
|
||||
},
|
||||
afterFunc: func(t *WithT, obj *sourcev1.GitRepository, artifact sourcev1.Artifact) {
|
||||
t.Expect(obj.GetArtifact()).ToNot(BeNil())
|
||||
t.Expect(obj.GetArtifact().Checksum).To(Equal("ef9c34eab0584035ac8b8a4070876954ea46f270250d60648672feef3e943426"))
|
||||
t.Expect(obj.Status.URL).ToNot(BeEmpty())
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'main/revision'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -764,9 +764,10 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
|
|||
t.Expect(obj.GetArtifact().Checksum).To(Equal("ef9c34eab0584035ac8b8a4070876954ea46f270250d60648672feef3e943426"))
|
||||
t.Expect(obj.Status.URL).ToNot(BeEmpty())
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'main/revision'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -784,20 +785,27 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
|
|||
t.Expect(err).NotTo(HaveOccurred())
|
||||
t.Expect(localPath).To(Equal(targetFile))
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "Stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, meta.SucceededReason, "stored artifact for revision 'main/revision'"),
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'main/revision'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Target path does not exists",
|
||||
dir: "testdata/git/foo",
|
||||
wantErr: true,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'main/revision'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Target path is not a directory",
|
||||
dir: "testdata/git/repository/foo.txt",
|
||||
wantErr: true,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReconcilingCondition, "NewRevision", "new upstream revision 'main/revision'"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -824,7 +832,7 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) {
|
|||
|
||||
artifact := testStorage.NewArtifactFor(obj.Kind, obj, "main/revision", "checksum.tar.gz")
|
||||
|
||||
got, err := r.reconcileArtifact(ctx, obj, artifact, tt.includes, tt.dir)
|
||||
got, err := r.reconcileArtifact(ctx, obj, &artifact, &tt.includes, tt.dir)
|
||||
g.Expect(obj.Status.Conditions).To(conditions.MatchConditions(tt.assertConditions))
|
||||
g.Expect(err != nil).To(Equal(tt.wantErr))
|
||||
g.Expect(got).To(Equal(tt.want))
|
||||
|
@ -865,7 +873,7 @@ func TestGitRepositoryReconciler_reconcileInclude(t *testing.T) {
|
|||
dependencies []dependency
|
||||
includes []include
|
||||
beforeFunc func(obj *sourcev1.GitRepository)
|
||||
want ctrl.Result
|
||||
want sreconcile.Result
|
||||
wantErr bool
|
||||
assertConditions []metav1.Condition
|
||||
}{
|
||||
|
@ -891,9 +899,9 @@ func TestGitRepositoryReconciler_reconcileInclude(t *testing.T) {
|
|||
{name: "a", toPath: "a/", shouldExist: true},
|
||||
{name: "b", toPath: "b/", shouldExist: true},
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "IncludeChange", "Included artifacts differ from last observed includes"),
|
||||
*conditions.TrueCondition(sourcev1.ArtifactOutdatedCondition, "IncludeChange", "included artifacts differ from last observed includes"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -903,7 +911,7 @@ func TestGitRepositoryReconciler_reconcileInclude(t *testing.T) {
|
|||
},
|
||||
wantErr: true,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.IncludeUnavailableCondition, "NotFound", "Could not get resource for include 'a': gitrepositories.source.toolkit.fluxcd.io \"a\" not found"),
|
||||
*conditions.TrueCondition(sourcev1.IncludeUnavailableCondition, "NotFound", "could not get resource for include 'a': gitrepositories.source.toolkit.fluxcd.io \"a\" not found"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -920,8 +928,9 @@ func TestGitRepositoryReconciler_reconcileInclude(t *testing.T) {
|
|||
includes: []include{
|
||||
{name: "a", toPath: "a/"},
|
||||
},
|
||||
wantErr: true,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.IncludeUnavailableCondition, "NoArtifact", "No artifact available for include 'a'"),
|
||||
*conditions.TrueCondition(sourcev1.IncludeUnavailableCondition, "NoArtifact", "no artifact available for include 'a'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -945,7 +954,7 @@ func TestGitRepositoryReconciler_reconcileInclude(t *testing.T) {
|
|||
beforeFunc: func(obj *sourcev1.GitRepository) {
|
||||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "NoArtifact", "")
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{},
|
||||
},
|
||||
}
|
||||
|
@ -980,12 +989,8 @@ func TestGitRepositoryReconciler_reconcileInclude(t *testing.T) {
|
|||
}
|
||||
|
||||
r := &GitRepositoryReconciler{
|
||||
Client: builder.Build(),
|
||||
EventRecorder: record.NewFakeRecorder(32),
|
||||
// Events: helper.Events{
|
||||
// Scheme: testEnv.GetScheme(),
|
||||
// EventRecorder: record.NewFakeRecorder(32),
|
||||
// },
|
||||
Client: builder.Build(),
|
||||
EventRecorder: record.NewFakeRecorder(32),
|
||||
Storage: storage,
|
||||
requeueDependency: dependencyInterval,
|
||||
}
|
||||
|
@ -1018,7 +1023,10 @@ func TestGitRepositoryReconciler_reconcileInclude(t *testing.T) {
|
|||
g.Expect(err).NotTo(HaveOccurred())
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
got, err := r.reconcileInclude(ctx, obj, tmpDir)
|
||||
var artifact sourcev1.Artifact
|
||||
var includes artifactSet
|
||||
|
||||
got, err := r.reconcileInclude(ctx, obj, &artifact, &includes, tmpDir)
|
||||
g.Expect(obj.GetConditions()).To(conditions.MatchConditions(tt.assertConditions))
|
||||
g.Expect(err != nil).To(Equal(tt.wantErr))
|
||||
g.Expect(got).To(Equal(tt.want))
|
||||
|
@ -1065,7 +1073,7 @@ func TestGitRepositoryReconciler_reconcileDelete(t *testing.T) {
|
|||
|
||||
got, err := r.reconcileDelete(ctx, obj)
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
g.Expect(got).To(Equal(ctrl.Result{}))
|
||||
g.Expect(got).To(Equal(sreconcile.ResultEmpty))
|
||||
g.Expect(controllerutil.ContainsFinalizer(obj, sourcev1.SourceFinalizer)).To(BeFalse())
|
||||
g.Expect(obj.Status.Artifact).To(BeNil())
|
||||
}
|
||||
|
@ -1076,7 +1084,7 @@ func TestGitRepositoryReconciler_verifyCommitSignature(t *testing.T) {
|
|||
secret *corev1.Secret
|
||||
commit git.Commit
|
||||
beforeFunc func(obj *sourcev1.GitRepository)
|
||||
want ctrl.Result
|
||||
want sreconcile.Result
|
||||
wantErr bool
|
||||
assertConditions []metav1.Condition
|
||||
}{
|
||||
|
@ -1104,9 +1112,9 @@ func TestGitRepositoryReconciler_verifyCommitSignature(t *testing.T) {
|
|||
},
|
||||
}
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "Verified signature of commit 'shasum'"),
|
||||
*conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of commit 'shasum'"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1132,7 +1140,7 @@ func TestGitRepositoryReconciler_verifyCommitSignature(t *testing.T) {
|
|||
},
|
||||
wantErr: true,
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, meta.FailedReason, "Signature verification of commit 'shasum' failed: failed to verify commit with any of the given key rings"),
|
||||
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, meta.FailedReason, "signature verification of commit 'shasum' failed: failed to verify commit with any of the given key rings"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1157,7 +1165,7 @@ func TestGitRepositoryReconciler_verifyCommitSignature(t *testing.T) {
|
|||
obj.Spec.Interval = metav1.Duration{Duration: interval}
|
||||
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Foo", "")
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{},
|
||||
},
|
||||
{
|
||||
|
@ -1167,7 +1175,7 @@ func TestGitRepositoryReconciler_verifyCommitSignature(t *testing.T) {
|
|||
obj.Spec.Verification = &sourcev1.GitRepositoryVerification{}
|
||||
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Foo", "")
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
want: sreconcile.ResultSuccess,
|
||||
assertConditions: []metav1.Condition{},
|
||||
},
|
||||
}
|
||||
|
@ -1233,7 +1241,7 @@ func TestGitRepositoryReconciler_ConditionsUpdate(t *testing.T) {
|
|||
name: "no condition",
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "Stored artifact for revision"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "stored artifact for revision"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1243,7 +1251,7 @@ func TestGitRepositoryReconciler_ConditionsUpdate(t *testing.T) {
|
|||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "Stored artifact for revision"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "stored artifact for revision"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1253,7 +1261,7 @@ func TestGitRepositoryReconciler_ConditionsUpdate(t *testing.T) {
|
|||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "Stored artifact for revision"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "stored artifact for revision"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1263,11 +1271,10 @@ func TestGitRepositoryReconciler_ConditionsUpdate(t *testing.T) {
|
|||
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, "Foo", "")
|
||||
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, "Foo", "")
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactOutdatedCondition, "Foo", "")
|
||||
conditions.MarkTrue(obj, sourcev1.ArtifactUnavailableCondition, "Foo", "")
|
||||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "Stored artifact for revision"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "stored artifact for revision"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1278,7 +1285,7 @@ func TestGitRepositoryReconciler_ConditionsUpdate(t *testing.T) {
|
|||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "Stored artifact for revision"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "stored artifact for revision"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1289,7 +1296,7 @@ func TestGitRepositoryReconciler_ConditionsUpdate(t *testing.T) {
|
|||
},
|
||||
want: ctrl.Result{RequeueAfter: interval},
|
||||
assertConditions: []metav1.Condition{
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "Stored artifact for revision"),
|
||||
*conditions.TrueCondition(meta.ReadyCondition, "Succeeded", "stored artifact for revision"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -9,6 +9,7 @@ require (
|
|||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7
|
||||
github.com/cyphar/filepath-securejoin v0.2.2
|
||||
github.com/darkowlzz/controller-check v0.0.0-20220119215126-648356cef22c
|
||||
github.com/elazarl/goproxy v0.0.0-20211114080932-d06c3be7c11b
|
||||
github.com/fluxcd/pkg/apis/meta v0.11.0-rc.3
|
||||
github.com/fluxcd/pkg/gittestserver v0.5.0
|
||||
|
@ -115,6 +116,7 @@ require (
|
|||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/klauspost/compress v1.13.5 // indirect
|
||||
github.com/klauspost/cpuid v1.3.1 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/lib/pq v1.10.0 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -231,6 +231,8 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S
|
|||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
|
||||
github.com/darkowlzz/controller-check v0.0.0-20220119215126-648356cef22c h1:pyp/Dvd1gYP/D3z1zs46h0YhYzFp0hjxw0XVIO9+vh4=
|
||||
github.com/darkowlzz/controller-check v0.0.0-20220119215126-648356cef22c/go.mod h1:haYO9UW76kUUKpIBbv3ydaU5wZ/7r0yqp61PGzVRSYU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -617,6 +619,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||
|
|
Loading…
Reference in New Issue