Skip to content

Commit 5e0329c

Browse files
committed
OCIRepoReconciler: no-op reconcile improvements
Introduce two new fields in OCIRepository.Status: - ObservedIgnore - ObservedLayerSelector These are needed to capture the configurations used in building the artifact and rebuild the artifacts when their values change. The considerations for this are similar to the GitRepository reconciler no-op clone implementation. Both reconcileSource and reconcileArtifact need to consider the source configuration change when deciding if the artifact in the storage is up-to-date. Adds tests for reconcileSource and reconcileArtifact for the noop cases. Signed-off-by: Sunny <[email protected]>
1 parent 8bc36bc commit 5e0329c

File tree

5 files changed

+470
-15
lines changed

5 files changed

+470
-15
lines changed

api/v1beta2/ocirepository_types.go

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

206+
// ObservedIgnore is the observed exclusion patterns used for constructing
207+
// the source artifact.
208+
// +optional
209+
ObservedIgnore *string `json:"observedIgnore,omitempty"`
210+
211+
// ObservedLayerSelector is the observed layer selector used for constructing
212+
// the source artifact.
213+
// +optional
214+
ObservedLayerSelector *OCILayerSelector `json:"observedLayerSelector,omitempty"`
215+
206216
meta.ReconcileRequestStatus `json:",inline"`
207217
}
208218

api/v1beta2/zz_generated.deepcopy.go

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,29 @@ spec:
312312
description: ObservedGeneration is the last observed generation.
313313
format: int64
314314
type: integer
315+
observedIgnore:
316+
description: ObservedIgnore is the observed exclusion patterns used
317+
for constructing the source artifact.
318+
type: string
319+
observedLayerSelector:
320+
description: ObservedLayerSelector is the observed layer selector
321+
used for constructing the source artifact.
322+
properties:
323+
mediaType:
324+
description: MediaType specifies the OCI media type of the layer
325+
which should be extracted from the OCI Artifact. The first layer
326+
matching this type is selected.
327+
type: string
328+
operation:
329+
description: Operation specifies how the selected layer should
330+
be processed. By default, the layer compressed content is extracted
331+
to storage. When the operation is set to 'copy', the layer compressed
332+
content is persisted to storage as it is.
333+
enum:
334+
- extract
335+
- copy
336+
type: string
337+
type: object
315338
url:
316339
description: URL is the download link for the artifact output of the
317340
last OCI Repository sync.

controllers/ocirepository_controller.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import (
4343
"k8s.io/apimachinery/pkg/types"
4444
"k8s.io/apimachinery/pkg/util/sets"
4545
kuberecorder "k8s.io/client-go/tools/record"
46+
"k8s.io/utils/pointer"
4647

4748
ctrl "sigs.k8s.io/controller-runtime"
4849
"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -418,8 +419,9 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
418419
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of revision %s", revision)
419420
}
420421

421-
// Skip pulling if the artifact revision hasn't changes
422-
if obj.GetArtifact().HasRevision(revision) {
422+
// Skip pulling if the artifact revision and the source configuration has
423+
// not changed.
424+
if obj.GetArtifact().HasRevision(revision) && !ociSourceConfigChanged(obj) {
423425
conditions.Delete(obj, sourcev1.FetchFailedCondition)
424426
return sreconcile.ResultSuccess, nil
425427
}
@@ -924,15 +926,15 @@ func (r *OCIRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *so
924926

925927
// Set the ArtifactInStorageCondition if there's no drift.
926928
defer func() {
927-
if obj.GetArtifact().HasRevision(artifact.Revision) {
929+
if obj.GetArtifact().HasRevision(artifact.Revision) && !ociSourceConfigChanged(obj) {
928930
conditions.Delete(obj, sourcev1.ArtifactOutdatedCondition)
929931
conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason,
930932
"stored artifact for digest '%s'", artifact.Revision)
931933
}
932934
}()
933935

934936
// The artifact is up-to-date
935-
if obj.GetArtifact().HasRevision(artifact.Revision) {
937+
if obj.GetArtifact().HasRevision(artifact.Revision) && !ociSourceConfigChanged(obj) {
936938
r.eventLogf(ctx, obj, events.EventTypeTrace, sourcev1.ArtifactUpToDateReason,
937939
"artifact up-to-date with remote revision: '%s'", artifact.Revision)
938940
return sreconcile.ResultSuccess, nil
@@ -994,9 +996,11 @@ func (r *OCIRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *so
994996
}
995997
}
996998

997-
// Record it on the object
999+
// Record the observations on the object.
9981000
obj.Status.Artifact = artifact.DeepCopy()
9991001
obj.Status.Artifact.Metadata = metadata.Metadata
1002+
obj.Status.ObservedIgnore = obj.Spec.Ignore
1003+
obj.Status.ObservedLayerSelector = obj.Spec.LayerSelector
10001004

10011005
// Update symlink on a "best effort" basis
10021006
url, err := r.Storage.Symlink(artifact, "latest.tar.gz")
@@ -1125,3 +1129,29 @@ func (r *OCIRepositoryReconciler) notify(ctx context.Context, oldObj, newObj *so
11251129
}
11261130
}
11271131
}
1132+
1133+
// ociSourceConfigChanged evaluates the current spec with the observations
1134+
// of the artifact in the status to determine if source configuration has
1135+
// changed and requires rebuilding the artifact.
1136+
func ociSourceConfigChanged(obj *sourcev1.OCIRepository) bool {
1137+
if !pointer.StringEqual(obj.Spec.Ignore, obj.Status.ObservedIgnore) {
1138+
return true
1139+
}
1140+
1141+
if !layerSelectorEqual(obj.Spec.LayerSelector, obj.Status.ObservedLayerSelector) {
1142+
return true
1143+
}
1144+
1145+
return false
1146+
}
1147+
1148+
// Based on k8s.io/utils/pointer/pointer.go pointer value equality.
1149+
func layerSelectorEqual(a, b *sourcev1.OCILayerSelector) bool {
1150+
if (a == nil) != (b == nil) {
1151+
return false
1152+
}
1153+
if a == nil {
1154+
return true
1155+
}
1156+
return *a == *b
1157+
}

0 commit comments

Comments
 (0)