@@ -18,12 +18,10 @@ package controllers
1818
1919import (
2020 "context"
21- "crypto/sha256"
2221 "errors"
2322 "fmt"
2423 "os"
2524 "path/filepath"
26- "strconv"
2725 "strings"
2826 "time"
2927
@@ -33,6 +31,7 @@ import (
3331 "k8s.io/apimachinery/pkg/runtime"
3432 "k8s.io/apimachinery/pkg/types"
3533 kuberecorder "k8s.io/client-go/tools/record"
34+ "k8s.io/utils/pointer"
3635 ctrl "sigs.k8s.io/controller-runtime"
3736 "sigs.k8s.io/controller-runtime/pkg/builder"
3837 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -507,8 +506,8 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
507506 // If it's a partial commit obtained from an existing artifact, check if the
508507 // reconciliation can be skipped if other configurations have not changed.
509508 if ! git .IsConcreteCommit (* commit ) {
510- // Calculate content configuration checksum .
511- if r . calculateContentConfigChecksum (obj , includes ) == obj . Status . ContentConfigChecksum {
509+ // Check if the source config contributing to the artifact has changed .
510+ if ! gitSourceConfigChanged (obj , includes ) {
512511 ge := serror .NewGeneric (
513512 fmt .Errorf ("no changes since last reconcilation: observed revision '%s'" ,
514513 commit .String ()), sourcev1 .GitOperationSucceedReason ,
@@ -559,27 +558,24 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
559558//
560559// The inspection of the given data to the object is differed, ensuring any
561560// stale observations like v1beta2.ArtifactOutdatedCondition are removed.
562- // If the given Artifact and/or artifactSet (includes) and the content config
563- // checksum do not differ from the object's current, it returns early.
561+ // If the given Artifact and/or artifactSet (includes) and observed source
562+ // config do not differ from the object's current, it returns early.
564563// Source ignore patterns are loaded, and the given directory is archived while
565564// taking these patterns into account.
566- // On a successful archive, the Artifact, Includes and new content config
567- // checksum in the Status of the object are set, and the symlink in the Storage
568- // is updated to its path.
565+ // On a successful archive, the Artifact, Includes, observed ignore, recurse
566+ // submodules and observed include in the Status of the object are set, and the
567+ // symlink in the Storage is updated to its path.
569568func (r * GitRepositoryReconciler ) reconcileArtifact (ctx context.Context ,
570569 obj * sourcev1.GitRepository , commit * git.Commit , includes * artifactSet , dir string ) (sreconcile.Result , error ) {
571570
572571 // Create potential new artifact with current available metadata
573572 artifact := r .Storage .NewArtifactFor (obj .Kind , obj .GetObjectMeta (), commit .String (), fmt .Sprintf ("%s.tar.gz" , commit .Hash .String ()))
574573
575- // Calculate the content config checksum.
576- ccc := r .calculateContentConfigChecksum (obj , includes )
577-
578574 // Set the ArtifactInStorageCondition if there's no drift.
579575 defer func () {
580576 if obj .GetArtifact ().HasRevision (artifact .Revision ) &&
581577 ! includes .Diff (obj .Status .IncludedArtifacts ) &&
582- obj . Status . ContentConfigChecksum == ccc {
578+ ! gitSourceConfigChanged ( obj , includes ) {
583579 conditions .Delete (obj , sourcev1 .ArtifactOutdatedCondition )
584580 conditions .MarkTrue (obj , sourcev1 .ArtifactInStorageCondition , meta .SucceededReason ,
585581 "stored artifact for revision '%s'" , artifact .Revision )
@@ -589,7 +585,7 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context,
589585 // The artifact is up-to-date
590586 if obj .GetArtifact ().HasRevision (artifact .Revision ) &&
591587 ! includes .Diff (obj .Status .IncludedArtifacts ) &&
592- obj . Status . ContentConfigChecksum == ccc {
588+ ! gitSourceConfigChanged ( obj , includes ) {
593589 r .eventLogf (ctx , obj , events .EventTypeTrace , sourcev1 .ArtifactUpToDateReason , "artifact up-to-date with remote revision: '%s'" , artifact .Revision )
594590 return sreconcile .ResultSuccess , nil
595591 }
@@ -652,10 +648,13 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context,
652648 return sreconcile .ResultEmpty , e
653649 }
654650
655- // Record it on the object
651+ // Record the observations on the object.
656652 obj .Status .Artifact = artifact .DeepCopy ()
657653 obj .Status .IncludedArtifacts = * includes
658- obj .Status .ContentConfigChecksum = ccc
654+ obj .Status .ContentConfigChecksum = "" // To be removed in the next API version.
655+ obj .Status .ObservedIgnore = obj .Spec .Ignore
656+ obj .Status .ObservedRecurseSubmodules = obj .Spec .RecurseSubmodules
657+ obj .Status .ObservedInclude = obj .Spec .Include
659658
660659 // Update symlink on a "best effort" basis
661660 url , err := r .Storage .Symlink (artifact , "latest.tar.gz" )
@@ -825,39 +824,6 @@ func (r *GitRepositoryReconciler) fetchIncludes(ctx context.Context, obj *source
825824 return & artifacts , nil
826825}
827826
828- // calculateContentConfigChecksum calculates a checksum of all the
829- // configurations that result in a change in the source artifact. It can be used
830- // to decide if further reconciliation is needed when an artifact already exists
831- // for a set of configurations.
832- func (r * GitRepositoryReconciler ) calculateContentConfigChecksum (obj * sourcev1.GitRepository , includes * artifactSet ) string {
833- c := []byte {}
834- // Consider the ignore rules and recurse submodules.
835- if obj .Spec .Ignore != nil {
836- c = append (c , []byte (* obj .Spec .Ignore )... )
837- }
838- c = append (c , []byte (strconv .FormatBool (obj .Spec .RecurseSubmodules ))... )
839-
840- // Consider the included repository attributes.
841- for _ , incl := range obj .Spec .Include {
842- c = append (c , []byte (incl .GitRepositoryRef .Name + incl .FromPath + incl .ToPath )... )
843- }
844-
845- // Consider the checksum and revision of all the included remote artifact.
846- // This ensures that if the included repos get updated, this checksum changes.
847- // NOTE: The content of an artifact may change at the same revision if the
848- // ignore rules change. Hence, consider both checksum and revision to
849- // capture changes in artifact checksum as well.
850- // TODO: Fix artifactSet.Diff() to consider checksum as well.
851- if includes != nil {
852- for _ , incl := range * includes {
853- c = append (c , []byte (incl .Checksum )... )
854- c = append (c , []byte (incl .Revision )... )
855- }
856- }
857-
858- return fmt .Sprintf ("sha256:%x" , sha256 .Sum256 (c ))
859- }
860-
861827// verifyCommitSignature verifies the signature of the given Git commit, if a
862828// verification mode is specified on the object.
863829// If the signature can not be verified or the verification fails, it records
@@ -978,3 +944,64 @@ func (r *GitRepositoryReconciler) eventLogf(ctx context.Context, obj runtime.Obj
978944 }
979945 r .Eventf (obj , eventType , reason , msg )
980946}
947+
948+ // gitSourceConfigChanged evaluates the current spec with the observations of
949+ // the artifact in the status to determine if source configuration has changed
950+ // and requires rebuilding the artifact.
951+ func gitSourceConfigChanged (obj * sourcev1.GitRepository , includes * artifactSet ) bool {
952+ if ! pointer .StringEqual (obj .Spec .Ignore , obj .Status .ObservedIgnore ) {
953+ return true
954+ }
955+ if obj .Spec .RecurseSubmodules != obj .Status .ObservedRecurseSubmodules {
956+ return true
957+ }
958+ if len (obj .Spec .Include ) != len (obj .Status .ObservedInclude ) {
959+ return true
960+ }
961+
962+ // Convert artifactSet to index addressable artifacts and ensure that it and
963+ // included artifacts include all the include from the spec.
964+ artifacts := []* sourcev1.Artifact (* includes )
965+ if len (obj .Spec .Include ) != len (artifacts ) {
966+ return true
967+ }
968+ if len (obj .Spec .Include ) != len (obj .Status .IncludedArtifacts ) {
969+ return true
970+ }
971+
972+ // The order of spec.include, status.IncludeArtifacts and
973+ // status.observedInclude are the same. Compare the values by index.
974+ for index , incl := range obj .Spec .Include {
975+ observedIncl := obj .Status .ObservedInclude [index ]
976+ observedInclArtifact := obj .Status .IncludedArtifacts [index ]
977+ currentIncl := artifacts [index ]
978+
979+ // Check if the include are the same in spec and status.
980+ if ! gitRepositoryIncludeEqual (incl , observedIncl ) {
981+ return true
982+ }
983+
984+ // Check if the included repositories are still the same.
985+ if observedInclArtifact .Revision != currentIncl .Revision {
986+ return true
987+ }
988+ if observedInclArtifact .Checksum != currentIncl .Checksum {
989+ return true
990+ }
991+ }
992+ return false
993+ }
994+
995+ // Returns true if both GitRepositoryIncludes are equal.
996+ func gitRepositoryIncludeEqual (a , b sourcev1.GitRepositoryInclude ) bool {
997+ if a .GitRepositoryRef != b .GitRepositoryRef {
998+ return false
999+ }
1000+ if a .FromPath != b .FromPath {
1001+ return false
1002+ }
1003+ if a .ToPath != b .ToPath {
1004+ return false
1005+ }
1006+ return true
1007+ }
0 commit comments