Skip to content

Commit

Permalink
Merge pull request #1338 from fluxcd/origin-revision
Browse files Browse the repository at this point in the history
Add OCI origin revision to events
  • Loading branch information
matheuscscp authored Jan 22, 2025
2 parents 550576e + eccdbad commit 5967686
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 22 deletions.
8 changes: 8 additions & 0 deletions api/v1/kustomization_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,14 @@ type KustomizationStatus struct {
// +optional
LastAppliedRevision string `json:"lastAppliedRevision,omitempty"`

// The last successfully applied origin revision.
// Equals the origin revision of the applied Artifact from the referenced Source.
// Usually present on the Metadata of the applied Artifact and depends on the
// Source type, e.g. for OCI it's the value associated with the key
// "org.opencontainers.image.revision".
// +optional
LastAppliedOriginRevision string `json:"lastAppliedOriginRevision,omitempty"`

// LastAttemptedRevision is the revision of the last reconciliation attempt.
// +optional
LastAttemptedRevision string `json:"lastAttemptedRevision,omitempty"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,14 @@ spec:
required:
- entries
type: object
lastAppliedOriginRevision:
description: |-
The last successfully applied origin revision.
Equals the origin revision of the applied Artifact from the referenced Source.
Usually present on the Metadata of the applied Artifact and depends on the
Source type, e.g. for OCI it's the value associated with the key
"org.opencontainers.image.revision".
type: string
lastAppliedRevision:
description: |-
The last successfully applied revision.
Expand Down
16 changes: 16 additions & 0 deletions docs/api/v1/kustomize.md
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,22 @@ Equals the Revision of the applied Artifact from the referenced Source.</p>
</tr>
<tr>
<td>
<code>lastAppliedOriginRevision</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>The last successfully applied origin revision.
Equals the origin revision of the applied Artifact from the referenced Source.
Usually present on the Metadata of the applied Artifact and depends on the
Source type, e.g. for OCI it&rsquo;s the value associated with the key
&ldquo;org.opencontainers.image.revision&rdquo;.</p>
</td>
</tr>
<tr>
<td>
<code>lastAttemptedRevision</code><br>
<em>
string
Expand Down
15 changes: 15 additions & 0 deletions docs/spec/v1/kustomizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -1915,6 +1915,21 @@ Status:
`.status.lastAppliedRevision` is the last revision of the Artifact from the
referred Source object that was successfully applied to the cluster.

### Last applied origin revision

`status.lastAppliedOriginRevision` is the last origin revision of the Artifact
from the referred Source object that was successfully applied to the cluster.

This field is usually retrieved from the Metadata of the Artifact and depends
on the Source type. For example, for OCI artifacts this is the value associated
with the standard metadata key `org.opencontainers.image.revision`, which is
used to track the revision of the source code that was used to build the OCI
artifact.

The controller will forward this value when emitting events in the metadata
key `originRevision`. The notification-controller will look for this key in
the event metadata when sending *commit status update* events to Git providers.

### Last attempted revision

`.status.lastAttemptedRevision` is the last revision of the Artifact from the
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/fluxcd/cli-utils v0.36.0-flux.11
github.com/fluxcd/kustomize-controller/api v1.4.0
github.com/fluxcd/pkg/apis/acl v0.5.0
github.com/fluxcd/pkg/apis/event v0.13.0
github.com/fluxcd/pkg/apis/event v0.15.0
github.com/fluxcd/pkg/apis/kustomize v1.8.0
github.com/fluxcd/pkg/apis/meta v1.9.0
github.com/fluxcd/pkg/http/fetch v0.14.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ github.com/fluxcd/cli-utils v0.36.0-flux.11 h1:W0y2uvCVkcE8bgV9jgoGSjzWbLFiNq1Aj
github.com/fluxcd/cli-utils v0.36.0-flux.11/go.mod h1:WZ7xUpZbK+O6HBxA5UWqzWTLSSltdmj4wS1LstS5Dqs=
github.com/fluxcd/pkg/apis/acl v0.5.0 h1:+ykKezgerKUlZwSYFUy03lPMOIAyWlqvMNNLIWWqOhk=
github.com/fluxcd/pkg/apis/acl v0.5.0/go.mod h1:IVDZx3MAoDWjlLrJHMF9Z27huFuXAEQlnbWw0M6EcTs=
github.com/fluxcd/pkg/apis/event v0.13.0 h1:m5qHAhYIC0+mRFy5OC8FZxBVBGJM3qxJ/sEg2Vgx4T8=
github.com/fluxcd/pkg/apis/event v0.13.0/go.mod h1:aRK2AONnjjSNW61B6Iy3SW4YHozACntnJeGm3fFqDqA=
github.com/fluxcd/pkg/apis/event v0.15.0 h1:k1suqIfVxnhEeKlGkvlHAbOYXjY8wRixT/OZcIuakqA=
github.com/fluxcd/pkg/apis/event v0.15.0/go.mod h1:aRK2AONnjjSNW61B6Iy3SW4YHozACntnJeGm3fFqDqA=
github.com/fluxcd/pkg/apis/kustomize v1.8.0 h1:HH6YRa3SMS72KK4cUyb9m5sK/dZH+Eti1qhjWDCgwKg=
github.com/fluxcd/pkg/apis/kustomize v1.8.0/go.mod h1:QCKIFj1ocdndaWSkrLs5JKvdGNYyTzQX1ZB3lYTwma0=
github.com/fluxcd/pkg/apis/meta v1.9.0 h1:wPgm7bWNJZ/ImS5GqikOxt362IgLPFBG73dZ27uWRiQ=
Expand Down
19 changes: 19 additions & 0 deletions internal/controller/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controller

const OCIArtifactOriginRevisionAnnotation = "org.opencontainers.image.revision"
54 changes: 38 additions & 16 deletions internal/controller/kustomization_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
time.Since(reconcileStart).String(),
obj.Spec.Interval.Duration.String())
log.Info(msg, "revision", obj.Status.LastAttemptedRevision)
r.event(obj, obj.Status.LastAppliedRevision, eventv1.EventSeverityInfo, msg,
r.event(obj, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityInfo, msg,
map[string]string{
kustomizev1.GroupVersion.Group + "/" + eventv1.MetaCommitStatusKey: eventv1.MetaCommitStatusUpdateValue,
})
Expand Down Expand Up @@ -234,7 +234,7 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
if acl.IsAccessDenied(err) {
conditions.MarkFalse(obj, meta.ReadyCondition, apiacl.AccessDeniedReason, "%s", err)
log.Error(err, "Access denied to cross-namespace source")
r.event(obj, "unknown", eventv1.EventSeverityError, err.Error(), nil)
r.event(obj, "", "", eventv1.EventSeverityError, err.Error(), nil)
return ctrl.Result{RequeueAfter: obj.GetRetryInterval()}, nil
}

Expand All @@ -249,14 +249,16 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
log.Info(msg)
return ctrl.Result{RequeueAfter: r.requeueDependency}, nil
}
revision := artifactSource.GetArtifact().Revision
originRevision := getOriginRevision(artifactSource)

// Check dependencies and requeue the reconciliation if the check fails.
if len(obj.Spec.DependsOn) > 0 {
if err := r.checkDependencies(ctx, obj, artifactSource); err != nil {
conditions.MarkFalse(obj, meta.ReadyCondition, meta.DependencyNotReadyReason, "%s", err)
msg := fmt.Sprintf("Dependencies do not meet ready condition, retrying in %s", r.requeueDependency.String())
log.Info(msg)
r.event(obj, artifactSource.GetArtifact().Revision, eventv1.EventSeverityInfo, msg, nil)
r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, msg, nil)
return ctrl.Result{RequeueAfter: r.requeueDependency}, nil
}
log.Info("All dependencies are ready, proceeding with reconciliation")
Expand All @@ -279,8 +281,8 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
time.Since(reconcileStart).String(),
obj.GetRetryInterval().String()),
"revision",
artifactSource.GetArtifact().Revision)
r.event(obj, artifactSource.GetArtifact().Revision, eventv1.EventSeverityError,
revision)
r.event(obj, revision, originRevision, eventv1.EventSeverityError,
reconcileErr.Error(), nil)
return ctrl.Result{RequeueAfter: obj.GetRetryInterval()}, nil
}
Expand All @@ -298,6 +300,7 @@ func (r *KustomizationReconciler) reconcile(

// Update status with the reconciliation progress.
revision := src.GetArtifact().Revision
originRevision := getOriginRevision(src)
progressingMsg := fmt.Sprintf("Fetching manifests for revision %s with a timeout of %s", revision, obj.GetTimeout().String())
conditions.MarkUnknown(obj, meta.ReadyCondition, meta.ProgressingReason, "%s", "Reconciliation in progress")
conditions.MarkReconciling(obj, meta.ProgressingReason, "%s", progressingMsg)
Expand Down Expand Up @@ -419,7 +422,7 @@ func (r *KustomizationReconciler) reconcile(
}

// Validate and apply resources in stages.
drifted, changeSet, err := r.apply(ctx, resourceManager, obj, revision, objects)
drifted, changeSet, err := r.apply(ctx, resourceManager, obj, revision, originRevision, objects)
if err != nil {
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err)
return err
Expand All @@ -444,7 +447,7 @@ func (r *KustomizationReconciler) reconcile(
}

// Run garbage collection for stale resources that do not have pruning disabled.
if _, err := r.prune(ctx, resourceManager, obj, revision, staleObjects); err != nil {
if _, err := r.prune(ctx, resourceManager, obj, revision, originRevision, staleObjects); err != nil {
conditions.MarkFalse(obj, meta.ReadyCondition, meta.PruneFailedReason, "%s", err)
return err
}
Expand All @@ -456,15 +459,17 @@ func (r *KustomizationReconciler) reconcile(
patcher,
obj,
revision,
originRevision,
isNewRevision,
drifted,
changeSet.ToObjMetadataSet()); err != nil {
conditions.MarkFalse(obj, meta.ReadyCondition, meta.HealthCheckFailedReason, "%s", err)
return err
}

// Set last applied revision.
// Set last applied revisions.
obj.Status.LastAppliedRevision = revision
obj.Status.LastAppliedOriginRevision = originRevision

// Mark the object as ready.
conditions.MarkTrue(obj,
Expand Down Expand Up @@ -656,6 +661,7 @@ func (r *KustomizationReconciler) apply(ctx context.Context,
manager *ssa.ResourceManager,
obj *kustomizev1.Kustomization,
revision string,
originRevision string,
objects []*unstructured.Unstructured) (bool, *ssa.ChangeSet, error) {
log := ctrl.LoggerFrom(ctx)

Expand Down Expand Up @@ -841,7 +847,7 @@ func (r *KustomizationReconciler) apply(ctx context.Context,
// emit event only if the server-side apply resulted in changes
applyLog := strings.TrimSuffix(changeSetLog.String(), "\n")
if applyLog != "" {
r.event(obj, revision, eventv1.EventSeverityInfo, applyLog, nil)
r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, applyLog, nil)
}

return applyLog != "", resultSet, nil
Expand All @@ -852,6 +858,7 @@ func (r *KustomizationReconciler) checkHealth(ctx context.Context,
patcher *patch.SerialPatcher,
obj *kustomizev1.Kustomization,
revision string,
originRevision string,
isNewRevision bool,
drifted bool,
objects object.ObjMetadataSet) error {
Expand Down Expand Up @@ -910,7 +917,7 @@ func (r *KustomizationReconciler) checkHealth(ctx context.Context,
// Emit recovery event if the previous health check failed.
msg := fmt.Sprintf("Health check passed in %s", time.Since(checkStart).String())
if !wasHealthy || (isNewRevision && drifted) {
r.event(obj, revision, eventv1.EventSeverityInfo, msg, nil)
r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, msg, nil)
}

conditions.MarkTrue(obj, meta.HealthyCondition, meta.SucceededReason, "%s", msg)
Expand All @@ -925,6 +932,7 @@ func (r *KustomizationReconciler) prune(ctx context.Context,
manager *ssa.ResourceManager,
obj *kustomizev1.Kustomization,
revision string,
originRevision string,
objects []*unstructured.Unstructured) (bool, error) {
if !obj.Spec.Prune {
return false, nil
Expand All @@ -949,7 +957,7 @@ func (r *KustomizationReconciler) prune(ctx context.Context,
// emit event only if the prune operation resulted in changes
if changeSet != nil && len(changeSet.Entries) > 0 {
log.Info(fmt.Sprintf("garbage collection completed: %s", changeSet.String()))
r.event(obj, revision, eventv1.EventSeverityInfo, changeSet.String(), nil)
r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, changeSet.String(), nil)
return true, nil
}

Expand Down Expand Up @@ -1004,19 +1012,19 @@ func (r *KustomizationReconciler) finalize(ctx context.Context,

changeSet, err := resourceManager.DeleteAll(ctx, objects, opts)
if err != nil {
r.event(obj, obj.Status.LastAppliedRevision, eventv1.EventSeverityError, "pruning for deleted resource failed", nil)
r.event(obj, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityError, "pruning for deleted resource failed", nil)
// Return the error so we retry the failed garbage collection
return ctrl.Result{}, err
}

if changeSet != nil && len(changeSet.Entries) > 0 {
r.event(obj, obj.Status.LastAppliedRevision, eventv1.EventSeverityInfo, changeSet.String(), nil)
r.event(obj, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityInfo, changeSet.String(), nil)
}
} else {
// when the account to impersonate is gone, log the stale objects and continue with the finalization
msg := fmt.Sprintf("unable to prune objects: \n%s", ssautil.FmtUnstructuredList(objects))
log.Error(fmt.Errorf("skiping pruning, failed to find account to impersonate"), msg)
r.event(obj, obj.Status.LastAppliedRevision, eventv1.EventSeverityError, msg, nil)
r.event(obj, obj.Status.LastAppliedRevision, obj.Status.LastAppliedOriginRevision, eventv1.EventSeverityError, msg, nil)
}
}

Expand All @@ -1027,13 +1035,16 @@ func (r *KustomizationReconciler) finalize(ctx context.Context,
}

func (r *KustomizationReconciler) event(obj *kustomizev1.Kustomization,
revision, severity, msg string,
revision, originRevision, severity, msg string,
metadata map[string]string) {
if metadata == nil {
metadata = map[string]string{}
}
if revision != "" {
metadata[kustomizev1.GroupVersion.Group+"/revision"] = revision
metadata[kustomizev1.GroupVersion.Group+"/"+eventv1.MetaRevisionKey] = revision
}
if originRevision != "" {
metadata[kustomizev1.GroupVersion.Group+"/"+eventv1.MetaOriginRevisionKey] = originRevision
}

reason := severity
Expand Down Expand Up @@ -1108,3 +1119,14 @@ func (r *KustomizationReconciler) patch(ctx context.Context,

return nil
}

// getOriginRevision returns the origin revision of the source artifact,
// or the empty string if it's not present, or if the artifact itself
// is not present.
func getOriginRevision(src sourcev1.Source) string {
a := src.GetArtifact()
if a == nil {
return ""
}
return a.Metadata[OCIArtifactOriginRevisionAnnotation]
}
Loading

0 comments on commit 5967686

Please sign in to comment.