Skip to content

Commit b1e7993

Browse files
committed
OCIRepo: Add observed content config in status
Replace content config checksum with explicit artifact content 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 34f127b commit b1e7993

File tree

7 files changed

+334
-68
lines changed

7 files changed

+334
-68
lines changed

api/v1beta2/ocirepository_types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,22 @@ type OCIRepositoryStatus struct {
211211
// be used to determine if the content configuration has changed and the
212212
// artifact needs to be rebuilt.
213213
// It has the format of `<algo>:<checksum>`, for example: `sha256:<checksum>`.
214+
//
215+
// Deprecated: Replaced with explicit fields for observed artifact content
216+
// config in the status.
214217
// +optional
215218
ContentConfigChecksum string `json:"contentConfigChecksum,omitempty"`
216219

220+
// ObservedIgnore is the observed exclusion patterns used for constructing
221+
// the source artifact.
222+
// +optional
223+
ObservedIgnore *string `json:"observedIgnore,omitempty"`
224+
225+
// ObservedLayerSelector is the observed layer selector used for constructing
226+
// the source artifact.
227+
// +optional
228+
ObservedLayerSelector *OCILayerSelector `json:"observedLayerSelector,omitempty"`
229+
217230
meta.ReconcileRequestStatus `json:",inline"`
218231
}
219232

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: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,14 @@ spec:
301301
type: object
302302
type: array
303303
contentConfigChecksum:
304-
description: 'ContentConfigChecksum is a checksum of all the configurations
304+
description: "ContentConfigChecksum is a checksum of all the configurations
305305
related to the content of the source artifact: - .spec.ignore -
306306
.spec.layerSelector observed in .status.observedGeneration version
307307
of the object. This can be used to determine if the content configuration
308308
has changed and the artifact needs to be rebuilt. It has the format
309-
of `<algo>:<checksum>`, for example: `sha256:<checksum>`.'
309+
of `<algo>:<checksum>`, for example: `sha256:<checksum>`. \n Deprecated:
310+
Replaced with explicit fields for observed artifact content config
311+
in the status."
310312
type: string
311313
lastHandledReconcileAt:
312314
description: LastHandledReconcileAt holds the value of the most recent
@@ -317,6 +319,29 @@ spec:
317319
description: ObservedGeneration is the last observed generation.
318320
format: int64
319321
type: integer
322+
observedIgnore:
323+
description: ObservedIgnore is the observed exclusion patterns used
324+
for constructing the source artifact.
325+
type: string
326+
observedLayerSelector:
327+
description: ObservedLayerSelector is the observed layer selector
328+
used for constructing the source artifact.
329+
properties:
330+
mediaType:
331+
description: MediaType specifies the OCI media type of the layer
332+
which should be extracted from the OCI Artifact. The first layer
333+
matching this type is selected.
334+
type: string
335+
operation:
336+
description: Operation specifies how the selected layer should
337+
be processed. By default, the layer compressed content is extracted
338+
to storage. When the operation is set to 'copy', the layer compressed
339+
content is persisted to storage as it is.
340+
enum:
341+
- extract
342+
- copy
343+
type: string
344+
type: object
320345
url:
321346
description: URL is the download link for the artifact output of the
322347
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) && !ociContentConfigChanged(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) && !ociContentConfigChanged(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) && !ociContentConfigChanged(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+
// ociContentConfigChanged evaluates the current spec with the observations
1191+
// of the artifact in the status to determine if artifact content configuration
1192+
// has changed and requires rebuilding the artifact.
1193+
func ociContentConfigChanged(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)