func()

in pkg/controller/tf/controller.go [259:412]


func (r *Reconciler) sync(ctx context.Context, krmResource *krmtotf.Resource) (requeue bool, err error) {
	// isolate any panics to only this function
	defer execution.RecoverWithInternalError(&err)
	if !krmResource.GetDeletionTimestamp().IsZero() {
		// Deleting
		r.logger.Info("finalizing resource deletion", "resource", k8s.GetNamespacedName(krmResource))
		if !k8s.HasFinalizer(krmResource, k8s.ControllerFinalizerName) {
			r.logger.Info("no controller finalizer is present; no finalization necessary",
				"resource", k8s.GetNamespacedName(krmResource))
			return false, nil
		}
		if k8s.HasFinalizer(krmResource, k8s.DeletionDefenderFinalizerName) {
			r.logger.Info("deletion defender has not yet been finalized; requeuing", "resource", k8s.GetNamespacedName(krmResource))
			return true, nil
		}
		if err := r.HandleDeleting(ctx, &krmResource.Resource); err != nil {
			return false, err
		}
		if k8s.HasAbandonAnnotation(krmResource) {
			r.logger.Info("deletion policy set to abandon; abandoning underlying resource", "resource", k8s.GetNamespacedName(krmResource))
			return false, r.handleDeleted(ctx, krmResource)
		}

		if krmtotf.ShouldResolveParentForDelete(krmResource) {
			orphaned, parent, err := r.isOrphaned(krmResource)
			// Handle orphaned resources
			if err != nil {
				return false, err
			}
			if orphaned {
				r.logger.Info("resource has been orphaned; no API call necessary", "resource", k8s.GetNamespacedName(krmResource))
				return false, r.handleDeleted(ctx, krmResource)
			}

			if parent != nil && !k8s.IsResourceReady(parent) {
				if krmtotf.ShouldCheckParentReadyForDelete(krmResource, parent) {
					// If this resource has a parent and is not orphaned, ensure its parent
					// is ready before attempting deletion.
					// Requeue resource for reconciliation with exponential backoff applied
					return true, r.HandleUnresolvableDeps(ctx, &krmResource.Resource, k8s.NewReferenceNotReadyErrorForResource(parent))
				}
			}
		}
		liveState, err := krmtotf.FetchLiveStateForDelete(ctx, krmResource, r.provider, r, r.smLoader)
		if err != nil {
			return false, r.HandleDeleteFailed(ctx, &krmResource.Resource, fmt.Errorf("error fetching live state: %w", err))
		}
		if liveState.Empty() {
			r.logger.Info("underlying resource does not exist; no API call necessary", "resource", k8s.GetNamespacedName(krmResource))
			return false, r.handleDeleted(ctx, krmResource)
		}
		if err := r.obtainResourceLeaseIfNecessary(ctx, krmResource, liveState); err != nil {
			return false, err
		}
		r.logger.Info("deleting underlying resource", "resource", k8s.GetNamespacedName(krmResource))
		if _, err := krmResource.TFResource.Apply(ctx, liveState, &terraform.InstanceDiff{Destroy: true}, r.provider.Meta()); err != nil {
			return false, r.HandleDeleteFailed(ctx, &krmResource.Resource, fmt.Errorf("error deleting resource: %v", err))
		}
		return false, r.handleDeleted(ctx, krmResource)
	}
	liveState, err := krmtotf.FetchLiveStateForCreateAndUpdate(ctx, krmResource, r.provider, r, r.smLoader)
	if err != nil {
		if unwrappedErr, ok := lifecyclehandler.CausedByUnresolvableDeps(err); ok {
			r.logger.Info(unwrappedErr.Error(), "resource", k8s.GetNamespacedName(krmResource))
			return r.handleUnresolvableDeps(ctx, &krmResource.Resource, unwrappedErr)
		}
		return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error fetching live state: %w", err))
	}
	if err := r.obtainResourceLeaseIfNecessary(ctx, krmResource, liveState); err != nil {
		return false, err
	}
	ok, err := r.hasServerGeneratedIDAndHadBeenCreatedOnceAlready(krmResource)
	if err != nil {
		return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, err)
	}
	if ok && liveState.Empty() {
		// GCP resource with server-generated ID had been created once already,
		// but no longer exists. Don't "recreate" the resource, since
		// "recreating" resources with server-generated IDs technically creates
		// a brand new resource instead.
		return false, r.HandleUpdateFailed(ctx, &krmResource.Resource,
			fmt.Errorf("underlying resource no longer exists and can't be recreated without creating a brand new resource"))
	}
	config, secretVersions, err := krmtotf.KRMResourceToTFResourceConfigFull(
		krmResource, r, r.smLoader, liveState, r.schemaRef.JSONSchema, true,
	)
	if err != nil {
		if unwrappedErr, ok := lifecyclehandler.CausedByUnresolvableDeps(err); ok {
			r.logger.Info(unwrappedErr.Error(), "resource", k8s.GetNamespacedName(krmResource))
			return r.handleUnresolvableDeps(ctx, &krmResource.Resource, unwrappedErr)
		}
		return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error expanding resource configuration for kind %s: %w", krmResource.Kind, err))
	}
	// Apply last-minute apply overrides
	if err := resourceoverrides.Handler.PreTerraformApply(ctx, krmResource.GroupVersionKind(), &operations.PreTerraformApply{KRMResource: krmResource, TerraformConfig: config, LiveState: liveState}); err != nil {
		return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error applying pre-apply transformation to resource: %w", err))
	}
	diff, err := krmResource.TFResource.Diff(ctx, liveState, config, r.provider.Meta())
	if err != nil {
		return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error calculating diff: %w", err))
	}
	if !liveState.Empty() && diff.RequiresNew() {
		return false, r.HandleUpdateFailed(ctx, &krmResource.Resource,
			k8s.NewImmutableFieldsMutationError(tfresource.ImmutableFieldsFromDiff(diff)))
	}
	if err := r.EnsureFinalizers(ctx, krmResource.Original, &krmResource.Resource, k8s.ControllerFinalizerName, k8s.DeletionDefenderFinalizerName); err != nil {
		return false, err
	}
	if diff.Empty() {
		r.logger.Info("underlying resource already up to date", "resource", k8s.GetNamespacedName(krmResource))
		return false, r.handleUpToDate(ctx, krmResource, liveState, secretVersions)
	}

	// Report diff to structured-reporting subsystem
	{
		report := &structuredreporting.Diff{}
		u, err := krmResource.MarshalAsUnstructured()
		if err != nil {
			log := log.FromContext(ctx)
			log.Error(err, "error reporting diff")
		}
		report.Object = u
		if diff != nil {
			for k, attr := range diff.Attributes {
				report.Fields = append(report.Fields, structuredreporting.DiffField{
					ID:  k,
					Old: attr.Old,
					New: attr.New,
				})
			}
		}
		report.IsNewObject = liveState.Empty()
		structuredreporting.ReportDiff(ctx, report)
	}

	r.logger.Info("creating/updating underlying resource", "resource", k8s.GetNamespacedName(krmResource))
	if err := r.HandleUpdating(ctx, &krmResource.Resource); err != nil {
		return false, err
	}
	// If creating a new resource, turn off RequiresNew in the diff. This is
	// done to prevent TF from clearing providerMeta (which contains blueprint
	// attribution information) during Apply() (b/193567082#comment15).
	if liveState.Empty() {
		for _, d := range diff.Attributes {
			d.RequiresNew = false
		}
	}
	newState, diagnostics := krmResource.TFResource.Apply(ctx, liveState, diff, r.provider.Meta())
	if err := krmtotf.NewErrorFromDiagnostics(diagnostics); err != nil {
		r.logger.Error(err, "error applying desired state", "resource", krmResource.GetNamespacedName())
		return false, r.HandleUpdateFailed(ctx, &krmResource.Resource, fmt.Errorf("error applying desired state: %w", err))
	}
	return false, r.handleUpToDate(ctx, krmResource, newState, secretVersions)
}