in internal/controllers/reconciliation/controller.go [162:282]
func (c *Controller) reconcileResource(ctx context.Context, comp *apiv1.Composition, prev, res *resource.Resource, current *unstructured.Unstructured) (bool, error) {
logger := logr.FromContextOrDiscard(ctx)
start := time.Now()
defer func() {
reconciliationLatency.Observe(float64(time.Since(start).Milliseconds()))
}()
if res.Deleted(comp) {
if current == nil || current.GetDeletionTimestamp() != nil {
return false, nil // already deleted - nothing to do
}
reconciliationActions.WithLabelValues("delete").Inc()
err := c.upstreamClient.Delete(ctx, current)
if err != nil {
return true, client.IgnoreNotFound(fmt.Errorf("deleting resource: %w", err))
}
logger.V(0).Info("deleted resource")
return true, nil
}
if res.Patch != nil && current == nil {
logger.V(1).Info("resource doesn't exist - skipping patch")
return false, nil
}
// Create the resource when it doesn't exist, should exist, and wouldn't be created later by server-side apply
if current == nil && (res.DisableUpdates || res.Replace || c.disableSSA) {
reconciliationActions.WithLabelValues("create").Inc()
err := c.upstreamClient.Create(ctx, res.Unstructured())
if err != nil {
return false, fmt.Errorf("creating resource: %w", err)
}
logger.V(0).Info("created resource")
return true, nil
}
if res.DisableUpdates {
return false, nil
}
// Apply Eno patches
if res.Patch != nil {
if !res.NeedsToBePatched(current) {
return false, nil
}
patch, err := json.Marshal(&res.Patch)
if err != nil {
return false, fmt.Errorf("encoding json patch: %w", err)
}
reconciliationActions.WithLabelValues("patch").Inc()
err = c.upstreamClient.Patch(ctx, current, client.RawPatch(types.JSONPatchType, patch))
if err != nil {
return false, fmt.Errorf("applying patch: %w", err)
}
logger.V(0).Info("patched resource", "resourceVersion", current.GetResourceVersion())
return true, nil
}
// Dry-run the update to see if it's needed
dryRun, err := c.update(ctx, res, current, true)
if err != nil {
return false, fmt.Errorf("dry-run applying update: %w", err)
}
if resource.Compare(dryRun, current) {
return false, nil // in sync
}
// When using server side apply, make sure we haven't lost any managedFields metadata.
// Eno should always remove fields that are no longer set by the synthesizer, even if another client messed with managedFields.
if current != nil && !c.disableSSA && !res.Replace {
var dryRunPrev *unstructured.Unstructured
if prev != nil {
dryRunPrev = prev.Unstructured()
err = c.upstreamClient.Patch(ctx, dryRunPrev, client.Apply, client.ForceOwnership, client.FieldOwner("eno"), client.DryRunAll)
if err != nil {
return false, fmt.Errorf("getting managed fields values for previous version: %w", err)
}
}
outOfSyncWithPrevious := dryRunPrev != nil && !resource.CompareEnoManagedFields(dryRunPrev.GetManagedFields(), current.GetManagedFields())
outOfSyncWithCurrent := !resource.CompareEnoManagedFields(dryRun.GetManagedFields(), current.GetManagedFields())
if (outOfSyncWithPrevious && outOfSyncWithCurrent) || (outOfSyncWithCurrent && dryRunPrev == nil) {
if dryRunPrev == nil {
current.SetManagedFields(
resource.MergeEnoManagedFields(
current.GetManagedFields(), dryRun.GetManagedFields()))
} else {
current.SetManagedFields(
resource.MergeEnoManagedFields(
current.GetManagedFields(), dryRunPrev.GetManagedFields()))
}
err := c.upstreamClient.Update(ctx, current, client.FieldOwner("eno"))
if err != nil {
return false, fmt.Errorf("updating managed fields metadata: %w", err)
}
logger.V(0).Info("corrected drift in managed fields metadata")
return true, nil
}
}
// Do the actual non-dryrun update
reconciliationActions.WithLabelValues("apply").Inc()
updated, err := c.update(ctx, res, current, false)
if err != nil {
return false, fmt.Errorf("applying update: %w", err)
}
if current != nil && updated.GetResourceVersion() == current.GetResourceVersion() {
logger.V(0).Info("resource didn't change after update")
return false, nil
}
if current != nil {
logger = logger.WithValues("oldResourceVersion", current.GetResourceVersion())
}
logger.V(0).Info("applied resource", "resourceVersion", updated.GetResourceVersion(), "dryRunResourceVersion", dryRun.GetResourceVersion())
return true, nil
}