Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GitRepositoryReconciler no-op clone improvements #721

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/v1beta2/condition_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,7 @@ const (

// CacheOperationFailedReason signals a failure in cache operation.
CacheOperationFailedReason string = "CacheOperationFailed"

// CopyOperationFailedReason signals a failure in copying files.
CopyOperationFailedReason string = "CopyOperationFailed"
)
68 changes: 58 additions & 10 deletions controllers/gitrepository_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ type GitRepositoryReconciler struct {
ControllerName string

requeueDependency time.Duration
features map[string]bool
}

type GitRepositoryReconcilerOptions struct {
Expand All @@ -134,6 +135,15 @@ func (r *GitRepositoryReconciler) SetupWithManager(mgr ctrl.Manager) error {
func (r *GitRepositoryReconciler) SetupWithManagerAndOptions(mgr ctrl.Manager, opts GitRepositoryReconcilerOptions) error {
r.requeueDependency = opts.DependencyRequeueInterval

if r.features == nil {
r.features = map[string]bool{}
}

// Check and enable gated features.
if oc, _ := features.Enabled(features.OptimizedGitClones); oc {
r.features[features.OptimizedGitClones] = true
}

return ctrl.NewControllerManagedBy(mgr).
For(&sourcev1.GitRepository{}, builder.WithPredicates(
predicate.Or(predicate.GenerationChangedPredicate{}, predicates.ReconcileRequestedPredicate{}),
Expand Down Expand Up @@ -294,7 +304,14 @@ func (r *GitRepositoryReconciler) notify(oldObj, newObj *sourcev1.GitRepository,
oldChecksum = oldObj.GetArtifact().Checksum
}

message := fmt.Sprintf("stored artifact for commit '%s'", commit.ShortMessage())
// A partial commit due to no-op clone doesn't contain the commit
// message information. Have separate message for it.
var message string
if git.IsConcreteCommit(commit) {
message = fmt.Sprintf("stored artifact for commit '%s'", commit.ShortMessage())
} else {
message = fmt.Sprintf("stored artifact for commit '%s'", commit.String())
}

// Notify on new artifact and failure recovery.
if oldChecksum != newObj.GetArtifact().Checksum {
Expand Down Expand Up @@ -414,9 +431,14 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
checkoutOpts.SemVer = ref.SemVer
}

if oc, _ := features.Enabled(features.OptimizedGitClones); oc {
if artifact := obj.GetArtifact(); artifact != nil {
checkoutOpts.LastRevision = artifact.Revision
if val, ok := r.features[features.OptimizedGitClones]; ok && val {
// Only if the object has an existing artifact in storage, attempt to
// short-circuit clone operation. reconcileStorage has already verified
// that the artifact exists.
if conditions.IsTrue(obj, sourcev1.ArtifactInStorageCondition) {
if artifact := obj.GetArtifact(); artifact != nil {
checkoutOpts.LastRevision = artifact.Revision
}
}
}

Expand Down Expand Up @@ -464,12 +486,6 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
defer cancel()
c, err := checkoutStrategy.Checkout(gitCtx, dir, repositoryURL, authOpts)
if err != nil {
var v git.NoChangesError
if errors.As(err, &v) {
return sreconcile.ResultSuccess,
&serror.Waiting{Err: v, Reason: v.Message, RequeueAfter: obj.GetRequeueAfter()}
}

e := &serror.Event{
Err: fmt.Errorf("failed to checkout and determine revision: %w", err),
Reason: sourcev1.GitOperationFailedReason,
Expand All @@ -480,8 +496,40 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
}
// Assign the commit to the shared commit reference.
*commit = *c

// If it's a partial commit obtained from an existing artifact, copy the
// artifact content into new source directory.
if !git.IsConcreteCommit(*commit) {
ctrl.LoggerFrom(ctx).V(logger.DebugLevel).Info(fmt.Sprintf(
"no changes since last reconciliation, observed revision '%s'", commit.String()))

// Remove the target directory, as CopyToPath() renames another
// directory to which the artifact is unpacked into the target
// directory. At this point, the target directory is empty, safe to
// remove.
if err := os.RemoveAll(dir); err != nil {
ctrl.LoggerFrom(ctx).Error(err, "failed to remove source build directory")
}
if err := r.Storage.CopyToPath(obj.GetArtifact(), "/", dir); err != nil {
e := &serror.Event{
Err: fmt.Errorf("failed to copy existing artifact to source dir: %w", err),
Reason: sourcev1.CopyOperationFailedReason,
}
conditions.MarkTrue(obj, sourcev1.StorageOperationFailedCondition, e.Reason, e.Err.Error())
return sreconcile.ResultEmpty, e
}
conditions.Delete(obj, sourcev1.FetchFailedCondition)
conditions.Delete(obj, sourcev1.StorageOperationFailedCondition)

return sreconcile.ResultSuccess, nil
}

ctrl.LoggerFrom(ctx).V(logger.DebugLevel).Info("git repository checked out", "url", obj.Spec.URL, "revision", commit.String())
conditions.Delete(obj, sourcev1.FetchFailedCondition)
// In case no-op clone resulted in a failure and in the subsequent
// reconciliation a new remote revision was observed, delete any stale
// StorageOperationFailedCondition.
conditions.Delete(obj, sourcev1.StorageOperationFailedCondition)

// Verify commit signature
if result, err := r.verifyCommitSignature(ctx, obj, *commit); err != nil || result == sreconcile.ResultEmpty {
Expand Down
Loading