Skip to content

Commit 62eb502

Browse files
committed
OCIRepo: Add observed source config in status
Replace content config checksum with explicit source config observations. It makes the observations of the controller more transparent and easier to debug. Introduces `observedIgnore` and `observedLayerSelector` status fields. Signed-off-by: Sunny <[email protected]>
1 parent f4de0a4 commit 62eb502

File tree

7 files changed

+325
-66
lines changed

7 files changed

+325
-66
lines changed

api/v1beta2/ocirepository_types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,16 @@ type OCIRepositoryStatus struct {
214214
// +optional
215215
ContentConfigChecksum string `json:"contentConfigChecksum,omitempty"`
216216

217+
// ObservedIgnore is the observed exclusion patterns used for constructing
218+
// the source artifact.
219+
// +optional
220+
ObservedIgnore *string `json:"observedIgnore,omitempty"`
221+
222+
// ObservedLayerSelector is the observed layer selector used for constructing
223+
// the source artifact.
224+
// +optional
225+
ObservedLayerSelector *OCILayerSelector `json:"observedLayerSelector,omitempty"`
226+
217227
meta.ReconcileRequestStatus `json:",inline"`
218228
}
219229

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
@@ -317,6 +317,29 @@ spec:
317317
description: ObservedGeneration is the last observed generation.
318318
format: int64
319319
type: integer
320+
observedIgnore:
321+
description: ObservedIgnore is the observed exclusion patterns used
322+
for constructing the source artifact.
323+
type: string
324+
observedLayerSelector:
325+
description: ObservedLayerSelector is the observed layer selector
326+
used for constructing the source artifact.
327+
properties:
328+
mediaType:
329+
description: MediaType specifies the OCI media type of the layer
330+
which should be extracted from the OCI Artifact. The first layer
331+
matching this type is selected.
332+
type: string
333+
operation:
334+
description: Operation specifies how the selected layer should
335+
be processed. By default, the layer compressed content is extracted
336+
to storage. When the operation is set to 'copy', the layer compressed
337+
content is persisted to storage as it is.
338+
enum:
339+
- extract
340+
- copy
341+
type: string
342+
type: object
320343
url:
321344
description: URL is the download link for the artifact output of the
322345
last OCI Repository sync.

controllers/ocirepository_controller.go

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

1919
import (
2020
"context"
21-
"crypto/sha256"
2221
"crypto/tls"
2322
"crypto/x509"
2423
"errors"
@@ -44,6 +43,7 @@ import (
4443
"k8s.io/apimachinery/pkg/types"
4544
"k8s.io/apimachinery/pkg/util/sets"
4645
kuberecorder "k8s.io/client-go/tools/record"
46+
"k8s.io/utils/pointer"
4747

4848
ctrl "sigs.k8s.io/controller-runtime"
4949
"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -427,10 +427,9 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, obj *sour
427427
conditions.MarkTrue(obj, sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of revision %s", revision)
428428
}
429429

430-
// Skip pulling if the artifact revision and the content config checksum has
430+
// Skip pulling if the artifact revision and the source configuration has
431431
// not changed.
432-
if obj.GetArtifact().HasRevision(revision) &&
433-
r.calculateContentConfigChecksum(obj) == obj.Status.ContentConfigChecksum {
432+
if obj.GetArtifact().HasRevision(revision) && !ociSourceConfigChanged(obj) {
434433
conditions.Delete(obj, sourcev1.FetchFailedCondition)
435434
return sreconcile.ResultSuccess, nil
436435
}
@@ -918,22 +917,17 @@ func (r *OCIRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *so
918917
artifact := r.Storage.NewArtifactFor(obj.Kind, obj, revision,
919918
fmt.Sprintf("%s.tar.gz", r.digestFromRevision(revision)))
920919

921-
// Calculate the content config checksum.
922-
ccc := r.calculateContentConfigChecksum(obj)
923-
924920
// Set the ArtifactInStorageCondition if there's no drift.
925921
defer func() {
926-
if obj.GetArtifact().HasRevision(artifact.Revision) &&
927-
obj.Status.ContentConfigChecksum == ccc {
922+
if obj.GetArtifact().HasRevision(artifact.Revision) && !ociSourceConfigChanged(obj) {
928923
conditions.Delete(obj, sourcev1.ArtifactOutdatedCondition)
929924
conditions.MarkTrue(obj, sourcev1.ArtifactInStorageCondition, meta.SucceededReason,
930925
"stored artifact for digest '%s'", artifact.Revision)
931926
}
932927
}()
933928

934929
// The artifact is up-to-date
935-
if obj.GetArtifact().HasRevision(artifact.Revision) &&
936-
obj.Status.ContentConfigChecksum == ccc {
930+
if obj.GetArtifact().HasRevision(artifact.Revision) && !ociSourceConfigChanged(obj) {
937931
r.eventLogf(ctx, obj, events.EventTypeTrace, sourcev1.ArtifactUpToDateReason,
938932
"artifact up-to-date with remote revision: '%s'", artifact.Revision)
939933
return sreconcile.ResultSuccess, nil
@@ -1008,10 +1002,12 @@ func (r *OCIRepositoryReconciler) reconcileArtifact(ctx context.Context, obj *so
10081002
}
10091003
}
10101004

1011-
// Record it on the object
1005+
// Record the observations on the object.
10121006
obj.Status.Artifact = artifact.DeepCopy()
10131007
obj.Status.Artifact.Metadata = metadata.Metadata
1014-
obj.Status.ContentConfigChecksum = ccc
1008+
obj.Status.ContentConfigChecksum = "" // To be removed in the next API version.
1009+
obj.Status.ObservedIgnore = obj.Spec.Ignore
1010+
obj.Status.ObservedLayerSelector = obj.Spec.LayerSelector
10151011

10161012
// Update symlink on a "best effort" basis
10171013
url, err := r.Storage.Symlink(artifact, "latest.tar.gz")
@@ -1141,24 +1137,6 @@ func (r *OCIRepositoryReconciler) notify(ctx context.Context, oldObj, newObj *so
11411137
}
11421138
}
11431139

1144-
// calculateContentConfigChecksum calculates a checksum of all the
1145-
// configurations that result in a change in the source artifact. It can be used
1146-
// to decide if further reconciliation is needed when an artifact already exists
1147-
// for a set of configurations.
1148-
func (r *OCIRepositoryReconciler) calculateContentConfigChecksum(obj *sourcev1.OCIRepository) string {
1149-
c := []byte{}
1150-
// Consider the ignore rules.
1151-
if obj.Spec.Ignore != nil {
1152-
c = append(c, []byte(*obj.Spec.Ignore)...)
1153-
}
1154-
// Consider the layer selector.
1155-
if obj.Spec.LayerSelector != nil {
1156-
c = append(c, []byte(obj.GetLayerMediaType()+obj.GetLayerOperation())...)
1157-
}
1158-
1159-
return fmt.Sprintf("sha256:%x", sha256.Sum256(c))
1160-
}
1161-
11621140
// craneOptions sets the auth headers, timeout and user agent
11631141
// for all operations against remote container registries.
11641142
func craneOptions(ctx context.Context, insecure bool) []crane.Option {
@@ -1208,3 +1186,31 @@ type remoteOptions struct {
12081186
craneOpts []crane.Option
12091187
verifyOpts []remote.Option
12101188
}
1189+
1190+
// ociSourceConfigChanged evaluates the current spec with the observations
1191+
// of the artifact in the status to determine if source configuration has
1192+
// changed and requires rebuilding the artifact.
1193+
func ociSourceConfigChanged(obj *sourcev1.OCIRepository) bool {
1194+
if !pointer.StringEqual(obj.Spec.Ignore, obj.Status.ObservedIgnore) {
1195+
return true
1196+
}
1197+
1198+
if !layerSelectorEqual(obj.Spec.LayerSelector, obj.Status.ObservedLayerSelector) {
1199+
return true
1200+
}
1201+
1202+
return false
1203+
}
1204+
1205+
// Returns true if both arguments are nil or both arguments
1206+
// dereference to the same value.
1207+
// Based on k8s.io/utils/pointer/pointer.go pointer value equality.
1208+
func layerSelectorEqual(a, b *sourcev1.OCILayerSelector) bool {
1209+
if (a == nil) != (b == nil) {
1210+
return false
1211+
}
1212+
if a == nil {
1213+
return true
1214+
}
1215+
return *a == *b
1216+
}

0 commit comments

Comments
 (0)