@@ -18,12 +18,10 @@ package controllers
18
18
19
19
import (
20
20
"context"
21
- "crypto/sha256"
22
21
"errors"
23
22
"fmt"
24
23
"os"
25
24
"path/filepath"
26
- "strconv"
27
25
"strings"
28
26
"time"
29
27
@@ -33,6 +31,7 @@ import (
33
31
"k8s.io/apimachinery/pkg/runtime"
34
32
"k8s.io/apimachinery/pkg/types"
35
33
kuberecorder "k8s.io/client-go/tools/record"
34
+ "k8s.io/utils/pointer"
36
35
ctrl "sigs.k8s.io/controller-runtime"
37
36
"sigs.k8s.io/controller-runtime/pkg/builder"
38
37
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -507,8 +506,8 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
507
506
// If it's a partial commit obtained from an existing artifact, check if the
508
507
// reconciliation can be skipped if other configurations have not changed.
509
508
if ! git .IsConcreteCommit (* commit ) {
510
- // Calculate content configuration checksum .
511
- if r . calculateContentConfigChecksum (obj , includes ) == obj . Status . ContentConfigChecksum {
509
+ // Check if the content config contributing to the artifact has changed .
510
+ if ! gitContentConfigChanged (obj , includes ) {
512
511
ge := serror .NewGeneric (
513
512
fmt .Errorf ("no changes since last reconcilation: observed revision '%s'" ,
514
513
commit .String ()), sourcev1 .GitOperationSucceedReason ,
@@ -559,27 +558,24 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
559
558
//
560
559
// The inspection of the given data to the object is differed, ensuring any
561
560
// 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 artifact
562
+ // content config do not differ from the object's current, it returns early.
564
563
// Source ignore patterns are loaded, and the given directory is archived while
565
564
// 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.
569
568
func (r * GitRepositoryReconciler ) reconcileArtifact (ctx context.Context ,
570
569
obj * sourcev1.GitRepository , commit * git.Commit , includes * artifactSet , dir string ) (sreconcile.Result , error ) {
571
570
572
571
// Create potential new artifact with current available metadata
573
572
artifact := r .Storage .NewArtifactFor (obj .Kind , obj .GetObjectMeta (), commit .String (), fmt .Sprintf ("%s.tar.gz" , commit .Hash .String ()))
574
573
575
- // Calculate the content config checksum.
576
- ccc := r .calculateContentConfigChecksum (obj , includes )
577
-
578
574
// Set the ArtifactInStorageCondition if there's no drift.
579
575
defer func () {
580
576
if obj .GetArtifact ().HasRevision (artifact .Revision ) &&
581
577
! includes .Diff (obj .Status .IncludedArtifacts ) &&
582
- obj . Status . ContentConfigChecksum == ccc {
578
+ ! gitContentConfigChanged ( obj , includes ) {
583
579
conditions .Delete (obj , sourcev1 .ArtifactOutdatedCondition )
584
580
conditions .MarkTrue (obj , sourcev1 .ArtifactInStorageCondition , meta .SucceededReason ,
585
581
"stored artifact for revision '%s'" , artifact .Revision )
@@ -589,7 +585,7 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context,
589
585
// The artifact is up-to-date
590
586
if obj .GetArtifact ().HasRevision (artifact .Revision ) &&
591
587
! includes .Diff (obj .Status .IncludedArtifacts ) &&
592
- obj . Status . ContentConfigChecksum == ccc {
588
+ ! gitContentConfigChanged ( obj , includes ) {
593
589
r .eventLogf (ctx , obj , events .EventTypeTrace , sourcev1 .ArtifactUpToDateReason , "artifact up-to-date with remote revision: '%s'" , artifact .Revision )
594
590
return sreconcile .ResultSuccess , nil
595
591
}
@@ -652,10 +648,13 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context,
652
648
return sreconcile .ResultEmpty , e
653
649
}
654
650
655
- // Record it on the object
651
+ // Record the observations on the object.
656
652
obj .Status .Artifact = artifact .DeepCopy ()
657
653
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
659
658
660
659
// Update symlink on a "best effort" basis
661
660
url , err := r .Storage .Symlink (artifact , "latest.tar.gz" )
@@ -825,39 +824,6 @@ func (r *GitRepositoryReconciler) fetchIncludes(ctx context.Context, obj *source
825
824
return & artifacts , nil
826
825
}
827
826
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
-
861
827
// verifyCommitSignature verifies the signature of the given Git commit, if a
862
828
// verification mode is specified on the object.
863
829
// 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
978
944
}
979
945
r .Eventf (obj , eventType , reason , msg )
980
946
}
947
+
948
+ // gitContentConfigChanged evaluates the current spec with the observations of
949
+ // the artifact in the status to determine if artifact content configuration has
950
+ // changed and requires rebuilding the artifact.
951
+ func gitContentConfigChanged (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
+ // the 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