func()

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
}