Skip to content

Commit 95cbf40

Browse files
authored
Merge pull request #917 from fluxcd/improv-ocirepo-optimized-reconcile
OCIRepositoryReconciler no-op improvements
2 parents 5ea4922 + dcd0db4 commit 95cbf40

File tree

5 files changed

+363
-15
lines changed

5 files changed

+363
-15
lines changed

api/v1beta2/ocirepository_types.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,17 @@ type OCIRepositoryStatus struct {
203203
// +optional
204204
Artifact *Artifact `json:"artifact,omitempty"`
205205

206+
// ContentConfigChecksum is a checksum of all the configurations related to
207+
// the content of the source artifact:
208+
// - .spec.ignore
209+
// - .spec.layerSelector
210+
// observed in .status.observedGeneration version of the object. This can
211+
// be used to determine if the content configuration has changed and the
212+
// artifact needs to be rebuilt.
213+
// It has the format of `<algo>:<checksum>`, for example: `sha256:<checksum>`.
214+
// +optional
215+
ContentConfigChecksum string `json:"contentConfigChecksum,omitempty"`
216+
206217
meta.ReconcileRequestStatus `json:",inline"`
207218
}
208219

config/crd/bases/source.toolkit.fluxcd.io_ocirepositories.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,14 @@ spec:
300300
- type
301301
type: object
302302
type: array
303+
contentConfigChecksum:
304+
description: 'ContentConfigChecksum is a checksum of all the configurations
305+
related to the content of the source artifact: - .spec.ignore -
306+
.spec.layerSelector observed in .status.observedGeneration version
307+
of the object. This can be used to determine if the content configuration
308+
has changed and the artifact needs to be rebuilt. It has the format
309+
of `<algo>:<checksum>`, for example: `sha256:<checksum>`.'
310+
type: string
303311
lastHandledReconcileAt:
304312
description: LastHandledReconcileAt holds the value of the most recent
305313
reconcile request value, so a change of the annotation value can

controllers/ocirepository_controller.go

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package controllers
1818

1919
import (
2020
"context"
21+
"crypto/sha256"
2122
"crypto/tls"
2223
"crypto/x509"
2324
"errors"
@@ -60,6 +61,7 @@ import (
6061
"github.com/fluxcd/pkg/runtime/events"
6162
"github.com/fluxcd/pkg/runtime/patch"
6263
"github.com/fluxcd/pkg/runtime/predicates"
64+
"github.com/fluxcd/pkg/sourceignore"
6365
"github.com/fluxcd/pkg/untar"
6466
"github.com/fluxcd/pkg/version"
6567
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
@@ -418,8 +420,10 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
418420
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of revision %s", revision)
419421
}
420422

421-
// Skip pulling if the artifact revision hasn't changes
422-
if obj.GetArtifact().HasRevision(revision) {
423+
// Skip pulling if the artifact revision and the content config checksum has
424+
// not changed.
425+
if obj.GetArtifact().HasRevision(revision) &&
426+
r.calculateContentConfigChecksum(obj) == obj.Status.ContentConfigChecksum {
423427
conditions.Delete(obj, sourcev1.FetchFailedCondition)
424428
return sreconcile.ResultSuccess, nil
425429
}
@@ -922,17 +926,22 @@ func (r *OCIRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *so
922926
artifact := r.Storage.NewArtifactFor(obj.Kind, obj, revision,
923927
fmt.Sprintf("%s.tar.gz", r.digestFromRevision(revision)))
924928

929+
// Calculate the content config checksum.
930+
ccc := r.calculateContentConfigChecksum(obj)
931+
925932
// Set the ArtifactInStorageCondition if there's no drift.
926933
defer func() {
927-
if obj.GetArtifact().HasRevision(artifact.Revision) {
934+
if obj.GetArtifact().HasRevision(artifact.Revision) &&
935+
obj.Status.ContentConfigChecksum == ccc {
928936
conditions.Delete(obj, sourcev1.ArtifactOutdatedCondition)
929937
conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason,
930938
"stored artifact for digest '%s'", artifact.Revision)
931939
}
932940
}()
933941

934942
// The artifact is up-to-date
935-
if obj.GetArtifact().HasRevision(artifact.Revision) {
943+
if obj.GetArtifact().HasRevision(artifact.Revision) &&
944+
obj.Status.ContentConfigChecksum == ccc {
936945
r.eventLogf(ctx, obj, events.EventTypeTrace, sourcev1.ArtifactUpToDateReason,
937946
"artifact up-to-date with remote revision: '%s'", artifact.Revision)
938947
return sreconcile.ResultSuccess, nil
@@ -984,7 +993,20 @@ func (r *OCIRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *so
984993
return sreconcile.ResultEmpty, e
985994
}
986995
default:
987-
if err := r.Storage.Archive(&artifact, dir, nil); err != nil {
996+
// Load ignore rules for archiving.
997+
ignoreDomain := strings.Split(dir, string(filepath.Separator))
998+
ps, err := sourceignore.LoadIgnorePatterns(dir, ignoreDomain)
999+
if err != nil {
1000+
return sreconcile.ResultEmpty, serror.NewGeneric(
1001+
fmt.Errorf("failed to load source ignore patterns from repository: %w", err),
1002+
"SourceIgnoreError",
1003+
)
1004+
}
1005+
if obj.Spec.Ignore != nil {
1006+
ps = append(ps, sourceignore.ReadPatterns(strings.NewReader(*obj.Spec.Ignore), ignoreDomain)...)
1007+
}
1008+
1009+
if err := r.Storage.Archive(&artifact, dir, SourceIgnoreFilter(ps, ignoreDomain)); err != nil {
9881010
e := serror.NewGeneric(
9891011
fmt.Errorf("unable to archive artifact to storage: %s", err),
9901012
sourcev1.ArchiveOperationFailedReason,
@@ -997,6 +1019,7 @@ func (r *OCIRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *so
9971019
// Record it on the object
9981020
obj.Status.Artifact = artifact.DeepCopy()
9991021
obj.Status.Artifact.Metadata = metadata.Metadata
1022+
obj.Status.ContentConfigChecksum = ccc
10001023

10011024
// Update symlink on a "best effort" basis
10021025
url, err := r.Storage.Symlink(artifact, "latest.tar.gz")
@@ -1125,3 +1148,21 @@ func (r *OCIRepositoryReconciler) notify(ctx context.Context, oldObj, newObj *so
11251148
}
11261149
}
11271150
}
1151+
1152+
// calculateContentConfigChecksum calculates a checksum of all the
1153+
// configurations that result in a change in the source artifact. It can be used
1154+
// to decide if further reconciliation is needed when an artifact already exists
1155+
// for a set of configurations.
1156+
func (r *OCIRepositoryReconciler) calculateContentConfigChecksum(obj *sourcev1.OCIRepository) string {
1157+
c := []byte{}
1158+
// Consider the ignore rules.
1159+
if obj.Spec.Ignore != nil {
1160+
c = append(c, []byte(*obj.Spec.Ignore)...)
1161+
}
1162+
// Consider the layer selector.
1163+
if obj.Spec.LayerSelector != nil {
1164+
c = append(c, []byte(obj.GetLayerMediaType()+obj.GetLayerOperation())...)
1165+
}
1166+
1167+
return fmt.Sprintf("sha256:%x", sha256.Sum256(c))
1168+
}

0 commit comments

Comments
 (0)